Home | History | Annotate | Download | only in i18n
      1 /*
      2 *******************************************************************************
      3 * Copyright (C) 2007-2014, International Business Machines Corporation and
      4 * others. All Rights Reserved.
      5 *******************************************************************************
      6 */
      7 
      8 #include "unicode/utypes.h"
      9 
     10 #if !UCONFIG_NO_FORMATTING
     11 
     12 #include <stdlib.h>
     13 
     14 #include "reldtfmt.h"
     15 #include "unicode/datefmt.h"
     16 #include "unicode/smpdtfmt.h"
     17 #include "unicode/msgfmt.h"
     18 #include "unicode/udisplaycontext.h"
     19 #include "unicode/uchar.h"
     20 #include "unicode/brkiter.h"
     21 
     22 #include "gregoimp.h" // for CalendarData
     23 #include "cmemory.h"
     24 #include "uresimp.h"
     25 
     26 U_NAMESPACE_BEGIN
     27 
     28 
     29 /**
     30  * An array of URelativeString structs is used to store the resource data loaded out of the bundle.
     31  */
     32 struct URelativeString {
     33     int32_t offset;         /** offset of this item, such as, the relative date **/
     34     int32_t len;            /** length of the string **/
     35     const UChar* string;    /** string, or NULL if not set **/
     36 };
     37 
     38 static const char DT_DateTimePatternsTag[]="DateTimePatterns";
     39 
     40 
     41 UOBJECT_DEFINE_RTTI_IMPLEMENTATION(RelativeDateFormat)
     42 
     43 RelativeDateFormat::RelativeDateFormat(const RelativeDateFormat& other) :
     44  DateFormat(other), fDateTimeFormatter(NULL), fDatePattern(other.fDatePattern),
     45  fTimePattern(other.fTimePattern), fCombinedFormat(NULL),
     46  fDateStyle(other.fDateStyle), fLocale(other.fLocale),
     47  fDayMin(other.fDayMin), fDayMax(other.fDayMax),
     48  fDatesLen(other.fDatesLen), fDates(NULL),
     49  fCombinedHasDateAtStart(other.fCombinedHasDateAtStart),
     50  fCapitalizationInfoSet(other.fCapitalizationInfoSet),
     51  fCapitalizationOfRelativeUnitsForUIListMenu(other.fCapitalizationOfRelativeUnitsForUIListMenu),
     52  fCapitalizationOfRelativeUnitsForStandAlone(other.fCapitalizationOfRelativeUnitsForStandAlone),
     53  fCapitalizationBrkIter(NULL)
     54 {
     55     if(other.fDateTimeFormatter != NULL) {
     56         fDateTimeFormatter = (SimpleDateFormat*)other.fDateTimeFormatter->clone();
     57     }
     58     if(other.fCombinedFormat != NULL) {
     59         fCombinedFormat = (MessageFormat*)other.fCombinedFormat->clone();
     60     }
     61     if (fDatesLen > 0) {
     62         fDates = (URelativeString*) uprv_malloc(sizeof(fDates[0])*fDatesLen);
     63         uprv_memcpy(fDates, other.fDates, sizeof(fDates[0])*fDatesLen);
     64     }
     65 #if !UCONFIG_NO_BREAK_ITERATION
     66     if (other.fCapitalizationBrkIter != NULL) {
     67         fCapitalizationBrkIter = (other.fCapitalizationBrkIter)->clone();
     68     }
     69 #endif
     70 }
     71 
     72 RelativeDateFormat::RelativeDateFormat( UDateFormatStyle timeStyle, UDateFormatStyle dateStyle,
     73                                         const Locale& locale, UErrorCode& status) :
     74  DateFormat(), fDateTimeFormatter(NULL), fDatePattern(), fTimePattern(), fCombinedFormat(NULL),
     75  fDateStyle(dateStyle), fLocale(locale), fDayMin(0), fDayMax(0), fDatesLen(0), fDates(NULL),
     76  fCombinedHasDateAtStart(FALSE), fCapitalizationInfoSet(FALSE),
     77  fCapitalizationOfRelativeUnitsForUIListMenu(FALSE), fCapitalizationOfRelativeUnitsForStandAlone(FALSE),
     78  fCapitalizationBrkIter(NULL)
     79 {
     80     if(U_FAILURE(status) ) {
     81         return;
     82     }
     83 
     84     if (timeStyle < UDAT_NONE || timeStyle > UDAT_SHORT) {
     85         // don't support other time styles (e.g. relative styles), for now
     86         status = U_ILLEGAL_ARGUMENT_ERROR;
     87         return;
     88     }
     89     UDateFormatStyle baseDateStyle = (dateStyle > UDAT_SHORT)? (UDateFormatStyle)(dateStyle & ~UDAT_RELATIVE): dateStyle;
     90     DateFormat * df;
     91     // Get fDateTimeFormatter from either date or time style (does not matter, we will override the pattern).
     92     // We do need to get separate patterns for the date & time styles.
     93     if (baseDateStyle != UDAT_NONE) {
     94         df = createDateInstance((EStyle)baseDateStyle, locale);
     95         fDateTimeFormatter=dynamic_cast<SimpleDateFormat *>(df);
     96         if (fDateTimeFormatter == NULL) {
     97             status = U_UNSUPPORTED_ERROR;
     98              return;
     99         }
    100         fDateTimeFormatter->toPattern(fDatePattern);
    101         if (timeStyle != UDAT_NONE) {
    102             df = createTimeInstance((EStyle)timeStyle, locale);
    103             SimpleDateFormat *sdf = dynamic_cast<SimpleDateFormat *>(df);
    104             if (sdf != NULL) {
    105                 sdf->toPattern(fTimePattern);
    106                 delete sdf;
    107             }
    108         }
    109     } else {
    110         // does not matter whether timeStyle is UDAT_NONE, we need something for fDateTimeFormatter
    111         df = createTimeInstance((EStyle)timeStyle, locale);
    112         fDateTimeFormatter=dynamic_cast<SimpleDateFormat *>(df);
    113         if (fDateTimeFormatter == NULL) {
    114             status = U_UNSUPPORTED_ERROR;
    115             return;
    116         }
    117         fDateTimeFormatter->toPattern(fTimePattern);
    118     }
    119 
    120     // Initialize the parent fCalendar, so that parse() works correctly.
    121     initializeCalendar(NULL, locale, status);
    122     loadDates(status);
    123 }
    124 
    125 RelativeDateFormat::~RelativeDateFormat() {
    126     delete fDateTimeFormatter;
    127     delete fCombinedFormat;
    128     uprv_free(fDates);
    129 #if !UCONFIG_NO_BREAK_ITERATION
    130     delete fCapitalizationBrkIter;
    131 #endif
    132 }
    133 
    134 
    135 Format* RelativeDateFormat::clone(void) const {
    136     return new RelativeDateFormat(*this);
    137 }
    138 
    139 UBool RelativeDateFormat::operator==(const Format& other) const {
    140     if(DateFormat::operator==(other)) {
    141         // The DateFormat::operator== check for fCapitalizationContext equality above
    142         //   is sufficient to check equality of all derived context-related data.
    143         // DateFormat::operator== guarantees following cast is safe
    144         RelativeDateFormat* that = (RelativeDateFormat*)&other;
    145         return (fDateStyle==that->fDateStyle   &&
    146                 fDatePattern==that->fDatePattern   &&
    147                 fTimePattern==that->fTimePattern   &&
    148                 fLocale==that->fLocale );
    149     }
    150     return FALSE;
    151 }
    152 
    153 static const UChar APOSTROPHE = (UChar)0x0027;
    154 
    155 UnicodeString& RelativeDateFormat::format(  Calendar& cal,
    156                                 UnicodeString& appendTo,
    157                                 FieldPosition& pos) const {
    158 
    159     UErrorCode status = U_ZERO_ERROR;
    160     UnicodeString relativeDayString;
    161     UDisplayContext capitalizationContext = getContext(UDISPCTX_TYPE_CAPITALIZATION, status);
    162 
    163     // calculate the difference, in days, between 'cal' and now.
    164     int dayDiff = dayDifference(cal, status);
    165 
    166     // look up string
    167     int32_t len = 0;
    168     const UChar *theString = getStringForDay(dayDiff, len, status);
    169     if(U_SUCCESS(status) && (theString!=NULL)) {
    170         // found a relative string
    171         relativeDayString.setTo(theString, len);
    172     }
    173 
    174     if ( relativeDayString.length() > 0 && !fDatePattern.isEmpty() &&
    175          (fTimePattern.isEmpty() || fCombinedFormat == NULL || fCombinedHasDateAtStart)) {
    176 #if !UCONFIG_NO_BREAK_ITERATION
    177         // capitalize relativeDayString according to context for relative, set formatter no context
    178         if ( u_islower(relativeDayString.char32At(0)) && fCapitalizationBrkIter!= NULL &&
    179              ( capitalizationContext==UDISPCTX_CAPITALIZATION_FOR_BEGINNING_OF_SENTENCE ||
    180                (capitalizationContext==UDISPCTX_CAPITALIZATION_FOR_UI_LIST_OR_MENU && fCapitalizationOfRelativeUnitsForUIListMenu) ||
    181                (capitalizationContext==UDISPCTX_CAPITALIZATION_FOR_STANDALONE && fCapitalizationOfRelativeUnitsForStandAlone) ) ) {
    182             // titlecase first word of relativeDayString
    183             relativeDayString.toTitle(fCapitalizationBrkIter, fLocale, U_TITLECASE_NO_LOWERCASE | U_TITLECASE_NO_BREAK_ADJUSTMENT);
    184         }
    185 #endif
    186         fDateTimeFormatter->setContext(UDISPCTX_CAPITALIZATION_NONE, status);
    187     } else {
    188         // set our context for the formatter
    189         fDateTimeFormatter->setContext(capitalizationContext, status);
    190     }
    191 
    192     if (fDatePattern.isEmpty()) {
    193         fDateTimeFormatter->applyPattern(fTimePattern);
    194         fDateTimeFormatter->format(cal,appendTo,pos);
    195     } else if (fTimePattern.isEmpty() || fCombinedFormat == NULL) {
    196         if (relativeDayString.length() > 0) {
    197             appendTo.append(relativeDayString);
    198         } else {
    199             fDateTimeFormatter->applyPattern(fDatePattern);
    200             fDateTimeFormatter->format(cal,appendTo,pos);
    201         }
    202     } else {
    203         UnicodeString datePattern;
    204         if (relativeDayString.length() > 0) {
    205             // Need to quote the relativeDayString to make it a legal date pattern
    206             relativeDayString.findAndReplace(UNICODE_STRING("'", 1), UNICODE_STRING("''", 2)); // double any existing APOSTROPHE
    207             relativeDayString.insert(0, APOSTROPHE); // add APOSTROPHE at beginning...
    208             relativeDayString.append(APOSTROPHE); // and at end
    209             datePattern.setTo(relativeDayString);
    210         } else {
    211             datePattern.setTo(fDatePattern);
    212         }
    213         UnicodeString combinedPattern;
    214         Formattable timeDatePatterns[] = { fTimePattern, datePattern };
    215         fCombinedFormat->format(timeDatePatterns, 2, combinedPattern, pos, status); // pos is ignored by this
    216         fDateTimeFormatter->applyPattern(combinedPattern);
    217         fDateTimeFormatter->format(cal,appendTo,pos);
    218     }
    219 
    220     return appendTo;
    221 }
    222 
    223 
    224 
    225 UnicodeString&
    226 RelativeDateFormat::format(const Formattable& obj,
    227                          UnicodeString& appendTo,
    228                          FieldPosition& pos,
    229                          UErrorCode& status) const
    230 {
    231     // this is just here to get around the hiding problem
    232     // (the previous format() override would hide the version of
    233     // format() on DateFormat that this function correspond to, so we
    234     // have to redefine it here)
    235     return DateFormat::format(obj, appendTo, pos, status);
    236 }
    237 
    238 
    239 void RelativeDateFormat::parse( const UnicodeString& text,
    240                     Calendar& cal,
    241                     ParsePosition& pos) const {
    242 
    243     int32_t startIndex = pos.getIndex();
    244     if (fDatePattern.isEmpty()) {
    245         // no date pattern, try parsing as time
    246         fDateTimeFormatter->applyPattern(fTimePattern);
    247         fDateTimeFormatter->parse(text,cal,pos);
    248     } else if (fTimePattern.isEmpty() || fCombinedFormat == NULL) {
    249         // no time pattern or way to combine, try parsing as date
    250         // first check whether text matches a relativeDayString
    251         UBool matchedRelative = FALSE;
    252         for (int n=0; n < fDatesLen && !matchedRelative; n++) {
    253             if (fDates[n].string != NULL &&
    254                     text.compare(startIndex, fDates[n].len, fDates[n].string) == 0) {
    255                 // it matched, handle the relative day string
    256                 UErrorCode status = U_ZERO_ERROR;
    257                 matchedRelative = TRUE;
    258 
    259                 // Set the calendar to now+offset
    260                 cal.setTime(Calendar::getNow(),status);
    261                 cal.add(UCAL_DATE,fDates[n].offset, status);
    262 
    263                 if(U_FAILURE(status)) {
    264                     // failure in setting calendar field, set offset to beginning of rel day string
    265                     pos.setErrorIndex(startIndex);
    266                 } else {
    267                     pos.setIndex(startIndex + fDates[n].len);
    268                 }
    269             }
    270         }
    271         if (!matchedRelative) {
    272             // just parse as normal date
    273             fDateTimeFormatter->applyPattern(fDatePattern);
    274             fDateTimeFormatter->parse(text,cal,pos);
    275         }
    276     } else {
    277         // Here we replace any relativeDayString in text with the equivalent date
    278         // formatted per fDatePattern, then parse text normally using the combined pattern.
    279         UnicodeString modifiedText(text);
    280         FieldPosition fPos;
    281         int32_t dateStart = 0, origDateLen = 0, modDateLen = 0;
    282         UErrorCode status = U_ZERO_ERROR;
    283         for (int n=0; n < fDatesLen; n++) {
    284             int32_t relativeStringOffset;
    285             if (fDates[n].string != NULL &&
    286                     (relativeStringOffset = modifiedText.indexOf(fDates[n].string, fDates[n].len, startIndex)) >= startIndex) {
    287                 // it matched, replace the relative date with a real one for parsing
    288                 UnicodeString dateString;
    289                 Calendar * tempCal = cal.clone();
    290 
    291                 // Set the calendar to now+offset
    292                 tempCal->setTime(Calendar::getNow(),status);
    293                 tempCal->add(UCAL_DATE,fDates[n].offset, status);
    294                 if(U_FAILURE(status)) {
    295                     pos.setErrorIndex(startIndex);
    296                     delete tempCal;
    297                     return;
    298                 }
    299 
    300                 fDateTimeFormatter->applyPattern(fDatePattern);
    301                 fDateTimeFormatter->format(*tempCal, dateString, fPos);
    302                 dateStart = relativeStringOffset;
    303                 origDateLen = fDates[n].len;
    304                 modDateLen = dateString.length();
    305                 modifiedText.replace(dateStart, origDateLen, dateString);
    306                 delete tempCal;
    307                 break;
    308             }
    309         }
    310         UnicodeString combinedPattern;
    311         Formattable timeDatePatterns[] = { fTimePattern, fDatePattern };
    312         fCombinedFormat->format(timeDatePatterns, 2, combinedPattern, fPos, status); // pos is ignored by this
    313         fDateTimeFormatter->applyPattern(combinedPattern);
    314         fDateTimeFormatter->parse(modifiedText,cal,pos);
    315 
    316         // Adjust offsets
    317         UBool noError = (pos.getErrorIndex() < 0);
    318         int32_t offset = (noError)? pos.getIndex(): pos.getErrorIndex();
    319         if (offset >= dateStart + modDateLen) {
    320             // offset at or after the end of the replaced text,
    321             // correct by the difference between original and replacement
    322             offset -= (modDateLen - origDateLen);
    323         } else if (offset >= dateStart) {
    324             // offset in the replaced text, set it to the beginning of that text
    325             // (i.e. the beginning of the relative day string)
    326             offset = dateStart;
    327         }
    328         if (noError) {
    329             pos.setIndex(offset);
    330         } else {
    331             pos.setErrorIndex(offset);
    332         }
    333     }
    334 }
    335 
    336 UDate
    337 RelativeDateFormat::parse( const UnicodeString& text,
    338                          ParsePosition& pos) const {
    339     // redefined here because the other parse() function hides this function's
    340     // cunterpart on DateFormat
    341     return DateFormat::parse(text, pos);
    342 }
    343 
    344 UDate
    345 RelativeDateFormat::parse(const UnicodeString& text, UErrorCode& status) const
    346 {
    347     // redefined here because the other parse() function hides this function's
    348     // counterpart on DateFormat
    349     return DateFormat::parse(text, status);
    350 }
    351 
    352 
    353 const UChar *RelativeDateFormat::getStringForDay(int32_t day, int32_t &len, UErrorCode &status) const {
    354     if(U_FAILURE(status)) {
    355         return NULL;
    356     }
    357 
    358     // Is it outside the resource bundle's range?
    359     if(day < fDayMin || day > fDayMax) {
    360         return NULL; // don't have it.
    361     }
    362 
    363     // Linear search the held strings
    364     for(int n=0;n<fDatesLen;n++) {
    365         if(fDates[n].offset == day) {
    366             len = fDates[n].len;
    367             return fDates[n].string;
    368         }
    369     }
    370 
    371     return NULL;  // not found.
    372 }
    373 
    374 UnicodeString&
    375 RelativeDateFormat::toPattern(UnicodeString& result, UErrorCode& status) const
    376 {
    377     if (!U_FAILURE(status)) {
    378         result.remove();
    379         if (fDatePattern.isEmpty()) {
    380             result.setTo(fTimePattern);
    381         } else if (fTimePattern.isEmpty() || fCombinedFormat == NULL) {
    382             result.setTo(fDatePattern);
    383         } else {
    384             Formattable timeDatePatterns[] = { fTimePattern, fDatePattern };
    385             FieldPosition pos;
    386             fCombinedFormat->format(timeDatePatterns, 2, result, pos, status);
    387         }
    388     }
    389     return result;
    390 }
    391 
    392 UnicodeString&
    393 RelativeDateFormat::toPatternDate(UnicodeString& result, UErrorCode& status) const
    394 {
    395     if (!U_FAILURE(status)) {
    396         result.remove();
    397         result.setTo(fDatePattern);
    398     }
    399     return result;
    400 }
    401 
    402 UnicodeString&
    403 RelativeDateFormat::toPatternTime(UnicodeString& result, UErrorCode& status) const
    404 {
    405     if (!U_FAILURE(status)) {
    406         result.remove();
    407         result.setTo(fTimePattern);
    408     }
    409     return result;
    410 }
    411 
    412 void
    413 RelativeDateFormat::applyPatterns(const UnicodeString& datePattern, const UnicodeString& timePattern, UErrorCode &status)
    414 {
    415     if (!U_FAILURE(status)) {
    416         fDatePattern.setTo(datePattern);
    417         fTimePattern.setTo(timePattern);
    418     }
    419 }
    420 
    421 const DateFormatSymbols*
    422 RelativeDateFormat::getDateFormatSymbols() const
    423 {
    424     return fDateTimeFormatter->getDateFormatSymbols();
    425 }
    426 
    427 // override the DateFormat implementation in order to
    428 // lazily initialize relevant items
    429 void
    430 RelativeDateFormat::setContext(UDisplayContext value, UErrorCode& status)
    431 {
    432     DateFormat::setContext(value, status);
    433     if (U_SUCCESS(status)) {
    434         if (!fCapitalizationInfoSet &&
    435                 (value==UDISPCTX_CAPITALIZATION_FOR_UI_LIST_OR_MENU || value==UDISPCTX_CAPITALIZATION_FOR_STANDALONE)) {
    436             initCapitalizationContextInfo(fLocale);
    437             fCapitalizationInfoSet = TRUE;
    438         }
    439 #if !UCONFIG_NO_BREAK_ITERATION
    440         if ( fCapitalizationBrkIter == NULL && (value==UDISPCTX_CAPITALIZATION_FOR_BEGINNING_OF_SENTENCE ||
    441                 (value==UDISPCTX_CAPITALIZATION_FOR_UI_LIST_OR_MENU && fCapitalizationOfRelativeUnitsForUIListMenu) ||
    442                 (value==UDISPCTX_CAPITALIZATION_FOR_STANDALONE && fCapitalizationOfRelativeUnitsForStandAlone)) ) {
    443             UErrorCode status = U_ZERO_ERROR;
    444             fCapitalizationBrkIter = BreakIterator::createSentenceInstance(fLocale, status);
    445             if (U_FAILURE(status)) {
    446                 delete fCapitalizationBrkIter;
    447                 fCapitalizationBrkIter = NULL;
    448             }
    449         }
    450 #endif
    451     }
    452 }
    453 
    454 void
    455 RelativeDateFormat::initCapitalizationContextInfo(const Locale& thelocale)
    456 {
    457 #if !UCONFIG_NO_BREAK_ITERATION
    458     const char * localeID = (thelocale != NULL)? thelocale.getBaseName(): NULL;
    459     UErrorCode status = U_ZERO_ERROR;
    460     UResourceBundle *rb = ures_open(NULL, localeID, &status);
    461     rb = ures_getByKeyWithFallback(rb, "contextTransforms", rb, &status);
    462     rb = ures_getByKeyWithFallback(rb, "relative", rb, &status);
    463     if (U_SUCCESS(status) && rb != NULL) {
    464         int32_t len = 0;
    465         const int32_t * intVector = ures_getIntVector(rb, &len, &status);
    466         if (U_SUCCESS(status) && intVector != NULL && len >= 2) {
    467             fCapitalizationOfRelativeUnitsForUIListMenu = intVector[0];
    468             fCapitalizationOfRelativeUnitsForStandAlone = intVector[1];
    469         }
    470     }
    471     ures_close(rb);
    472 #endif
    473 }
    474 
    475 static const UChar patItem1[] = {0x7B,0x31,0x7D}; // "{1}"
    476 static const int32_t patItem1Len = 3;
    477 
    478 void RelativeDateFormat::loadDates(UErrorCode &status) {
    479     CalendarData calData(fLocale, "gregorian", status);
    480 
    481     UErrorCode tempStatus = status;
    482     UResourceBundle *dateTimePatterns = calData.getByKey(DT_DateTimePatternsTag, tempStatus);
    483     if(U_SUCCESS(tempStatus)) {
    484         int32_t patternsSize = ures_getSize(dateTimePatterns);
    485         if (patternsSize > kDateTime) {
    486             int32_t resStrLen = 0;
    487 
    488             int32_t glueIndex = kDateTime;
    489             if (patternsSize >= (DateFormat::kDateTimeOffset + DateFormat::kShort + 1)) {
    490                 // Get proper date time format
    491                 switch (fDateStyle) {
    492                 case kFullRelative:
    493                 case kFull:
    494                     glueIndex = kDateTimeOffset + kFull;
    495                     break;
    496                 case kLongRelative:
    497                 case kLong:
    498                     glueIndex = kDateTimeOffset + kLong;
    499                     break;
    500                 case kMediumRelative:
    501                 case kMedium:
    502                     glueIndex = kDateTimeOffset + kMedium;
    503                     break;
    504                 case kShortRelative:
    505                 case kShort:
    506                     glueIndex = kDateTimeOffset + kShort;
    507                     break;
    508                 default:
    509                     break;
    510                 }
    511             }
    512 
    513             const UChar *resStr = ures_getStringByIndex(dateTimePatterns, glueIndex, &resStrLen, &tempStatus);
    514             if (U_SUCCESS(tempStatus) && resStrLen >= patItem1Len && u_strncmp(resStr,patItem1,patItem1Len)==0) {
    515                 fCombinedHasDateAtStart = TRUE;
    516             }
    517             fCombinedFormat = new MessageFormat(UnicodeString(TRUE, resStr, resStrLen), fLocale, tempStatus);
    518         }
    519     }
    520 
    521     UResourceBundle *rb = ures_open(NULL, fLocale.getBaseName(), &status);
    522     rb = ures_getByKeyWithFallback(rb, "fields", rb, &status);
    523     rb = ures_getByKeyWithFallback(rb, "day", rb, &status);
    524     rb = ures_getByKeyWithFallback(rb, "relative", rb, &status);
    525     // set up min/max
    526     fDayMin=-1;
    527     fDayMax=1;
    528 
    529     if(U_FAILURE(status)) {
    530         fDatesLen=0;
    531         ures_close(rb);
    532         return;
    533     }
    534 
    535     fDatesLen = ures_getSize(rb);
    536     fDates = (URelativeString*) uprv_malloc(sizeof(fDates[0])*fDatesLen);
    537 
    538     // Load in each item into the array...
    539     int n = 0;
    540 
    541     UResourceBundle *subString = NULL;
    542 
    543     while(ures_hasNext(rb) && U_SUCCESS(status)) {  // iterate over items
    544         subString = ures_getNextResource(rb, subString, &status);
    545 
    546         if(U_FAILURE(status) || (subString==NULL)) break;
    547 
    548         // key = offset #
    549         const char *key = ures_getKey(subString);
    550 
    551         // load the string and length
    552         int32_t aLen;
    553         const UChar* aString = ures_getString(subString, &aLen, &status);
    554 
    555         if(U_FAILURE(status) || aString == NULL) break;
    556 
    557         // calculate the offset
    558         int32_t offset = atoi(key);
    559 
    560         // set min/max
    561         if(offset < fDayMin) {
    562             fDayMin = offset;
    563         }
    564         if(offset > fDayMax) {
    565             fDayMax = offset;
    566         }
    567 
    568         // copy the string pointer
    569         fDates[n].offset = offset;
    570         fDates[n].string = aString;
    571         fDates[n].len = aLen;
    572 
    573         n++;
    574     }
    575     ures_close(subString);
    576     ures_close(rb);
    577 
    578     // the fDates[] array could be sorted here, for direct access.
    579 }
    580 
    581 //----------------------------------------------------------------------
    582 
    583 // this should to be in DateFormat, instead it was copied from SimpleDateFormat.
    584 
    585 Calendar*
    586 RelativeDateFormat::initializeCalendar(TimeZone* adoptZone, const Locale& locale, UErrorCode& status)
    587 {
    588     if(!U_FAILURE(status)) {
    589         fCalendar = Calendar::createInstance(adoptZone?adoptZone:TimeZone::createDefault(), locale, status);
    590     }
    591     if (U_SUCCESS(status) && fCalendar == NULL) {
    592         status = U_MEMORY_ALLOCATION_ERROR;
    593     }
    594     return fCalendar;
    595 }
    596 
    597 int32_t RelativeDateFormat::dayDifference(Calendar &cal, UErrorCode &status) {
    598     if(U_FAILURE(status)) {
    599         return 0;
    600     }
    601     // TODO: Cache the nowCal to avoid heap allocs? Would be difficult, don't know the calendar type
    602     Calendar *nowCal = cal.clone();
    603     nowCal->setTime(Calendar::getNow(), status);
    604 
    605     // For the day difference, we are interested in the difference in the (modified) julian day number
    606     // which is midnight to midnight.  Using fieldDifference() is NOT correct here, because
    607     // 6pm Jan 4th  to 10am Jan 5th should be considered "tomorrow".
    608     int32_t dayDiff = cal.get(UCAL_JULIAN_DAY, status) - nowCal->get(UCAL_JULIAN_DAY, status);
    609 
    610     delete nowCal;
    611     return dayDiff;
    612 }
    613 
    614 U_NAMESPACE_END
    615 
    616 #endif
    617 
    618