intl/icu/source/i18n/reldtfmt.cpp

Wed, 31 Dec 2014 06:09:35 +0100

author
Michael Schloh von Bennewitz <michael@schloh.com>
date
Wed, 31 Dec 2014 06:09:35 +0100
changeset 0
6474c204b198
permissions
-rw-r--r--

Cloned upstream origin tor-browser at tor-browser-31.3.0esr-4.5-1-build1
revision ID fc1c9ff7c1b2defdbc039f12214767608f46423f for hacking purpose.

     1 /*
     2 *******************************************************************************
     3 * Copyright (C) 2007-2013, International Business Machines Corporation and
     4 * others. All Rights Reserved.
     5 *******************************************************************************
     6 */
     8 #include "unicode/utypes.h"
    10 #if !UCONFIG_NO_FORMATTING
    12 #include <stdlib.h>
    14 #include "reldtfmt.h"
    15 #include "unicode/datefmt.h"
    16 #include "unicode/smpdtfmt.h"
    17 #include "unicode/msgfmt.h"
    19 #include "gregoimp.h" // for CalendarData
    20 #include "cmemory.h"
    21 #include "uresimp.h"
    23 U_NAMESPACE_BEGIN
    26 /**
    27  * An array of URelativeString structs is used to store the resource data loaded out of the bundle.
    28  */
    29 struct URelativeString {
    30     int32_t offset;         /** offset of this item, such as, the relative date **/
    31     int32_t len;            /** length of the string **/
    32     const UChar* string;    /** string, or NULL if not set **/
    33 };
    35 static const char DT_DateTimePatternsTag[]="DateTimePatterns";
    38 UOBJECT_DEFINE_RTTI_IMPLEMENTATION(RelativeDateFormat)
    40 RelativeDateFormat::RelativeDateFormat(const RelativeDateFormat& other) :
    41  DateFormat(other), fDateTimeFormatter(NULL), fDatePattern(other.fDatePattern),
    42  fTimePattern(other.fTimePattern), fCombinedFormat(NULL),
    43  fDateStyle(other.fDateStyle), fLocale(other.fLocale),
    44  fDayMin(other.fDayMin), fDayMax(other.fDayMax),
    45  fDatesLen(other.fDatesLen), fDates(NULL)
    46 {
    47     if(other.fDateTimeFormatter != NULL) {
    48         fDateTimeFormatter = (SimpleDateFormat*)other.fDateTimeFormatter->clone();
    49     }
    50     if(other.fCombinedFormat != NULL) {
    51         fCombinedFormat = (MessageFormat*)other.fCombinedFormat->clone();
    52     }
    53     if (fDatesLen > 0) {
    54         fDates = (URelativeString*) uprv_malloc(sizeof(fDates[0])*fDatesLen);
    55         uprv_memcpy(fDates, other.fDates, sizeof(fDates[0])*fDatesLen);
    56     }
    57 }
    59 RelativeDateFormat::RelativeDateFormat( UDateFormatStyle timeStyle, UDateFormatStyle dateStyle,
    60                                         const Locale& locale, UErrorCode& status) :
    61  DateFormat(), fDateTimeFormatter(NULL), fDatePattern(), fTimePattern(), fCombinedFormat(NULL),
    62  fDateStyle(dateStyle), fLocale(locale), fDatesLen(0), fDates(NULL)
    63 {
    64     if(U_FAILURE(status) ) {
    65         return;
    66     }
    68     if (timeStyle < UDAT_NONE || timeStyle > UDAT_SHORT) {
    69         // don't support other time styles (e.g. relative styles), for now
    70         status = U_ILLEGAL_ARGUMENT_ERROR;
    71         return;
    72     }
    73     UDateFormatStyle baseDateStyle = (dateStyle > UDAT_SHORT)? (UDateFormatStyle)(dateStyle & ~UDAT_RELATIVE): dateStyle;
    74     DateFormat * df;
    75     // Get fDateTimeFormatter from either date or time style (does not matter, we will override the pattern).
    76     // We do need to get separate patterns for the date & time styles.
    77     if (baseDateStyle != UDAT_NONE) {
    78         df = createDateInstance((EStyle)baseDateStyle, locale);
    79         fDateTimeFormatter=dynamic_cast<SimpleDateFormat *>(df);
    80         if (fDateTimeFormatter == NULL) {
    81             status = U_UNSUPPORTED_ERROR;
    82              return;
    83         }
    84         fDateTimeFormatter->toPattern(fDatePattern);
    85         if (timeStyle != UDAT_NONE) {
    86             df = createTimeInstance((EStyle)timeStyle, locale);
    87             SimpleDateFormat *sdf = dynamic_cast<SimpleDateFormat *>(df);
    88             if (sdf != NULL) {
    89                 sdf->toPattern(fTimePattern);
    90                 delete sdf;
    91             }
    92         }
    93     } else {
    94         // does not matter whether timeStyle is UDAT_NONE, we need something for fDateTimeFormatter
    95         df = createTimeInstance((EStyle)timeStyle, locale);
    96         fDateTimeFormatter=dynamic_cast<SimpleDateFormat *>(df);
    97         if (fDateTimeFormatter == NULL) {
    98             status = U_UNSUPPORTED_ERROR;
    99             return;
   100         }
   101         fDateTimeFormatter->toPattern(fTimePattern);
   102     }
   104     // Initialize the parent fCalendar, so that parse() works correctly.
   105     initializeCalendar(NULL, locale, status);
   106     loadDates(status);
   107 }
   109 RelativeDateFormat::~RelativeDateFormat() {
   110     delete fDateTimeFormatter;
   111     delete fCombinedFormat;
   112     uprv_free(fDates);
   113 }
   116 Format* RelativeDateFormat::clone(void) const {
   117     return new RelativeDateFormat(*this);
   118 }
   120 UBool RelativeDateFormat::operator==(const Format& other) const {
   121     if(DateFormat::operator==(other)) {
   122         // DateFormat::operator== guarantees following cast is safe
   123         RelativeDateFormat* that = (RelativeDateFormat*)&other;
   124         return (fDateStyle==that->fDateStyle   &&
   125                 fDatePattern==that->fDatePattern   &&
   126                 fTimePattern==that->fTimePattern   &&
   127                 fLocale==that->fLocale);
   128     }
   129     return FALSE;
   130 }
   132 static const UChar APOSTROPHE = (UChar)0x0027;
   134 UnicodeString& RelativeDateFormat::format(  Calendar& cal,
   135                                 UnicodeString& appendTo,
   136                                 FieldPosition& pos) const {
   138     UErrorCode status = U_ZERO_ERROR;
   139     UnicodeString relativeDayString;
   141     // calculate the difference, in days, between 'cal' and now.
   142     int dayDiff = dayDifference(cal, status);
   144     // look up string
   145     int32_t len = 0;
   146     const UChar *theString = getStringForDay(dayDiff, len, status);
   147     if(U_SUCCESS(status) && (theString!=NULL)) {
   148         // found a relative string
   149         relativeDayString.setTo(theString, len);
   150     }
   152     if (fDatePattern.isEmpty()) {
   153         fDateTimeFormatter->applyPattern(fTimePattern);
   154         fDateTimeFormatter->format(cal,appendTo,pos);
   155     } else if (fTimePattern.isEmpty() || fCombinedFormat == NULL) {
   156         if (relativeDayString.length() > 0) {
   157             appendTo.append(relativeDayString);
   158         } else {
   159             fDateTimeFormatter->applyPattern(fDatePattern);
   160             fDateTimeFormatter->format(cal,appendTo,pos);
   161         }
   162     } else {
   163         UnicodeString datePattern;
   164         if (relativeDayString.length() > 0) {
   165             // Need to quote the relativeDayString to make it a legal date pattern
   166             relativeDayString.findAndReplace(UNICODE_STRING("'", 1), UNICODE_STRING("''", 2)); // double any existing APOSTROPHE
   167             relativeDayString.insert(0, APOSTROPHE); // add APOSTROPHE at beginning...
   168             relativeDayString.append(APOSTROPHE); // and at end
   169             datePattern.setTo(relativeDayString);
   170         } else {
   171             datePattern.setTo(fDatePattern);
   172         }
   173         UnicodeString combinedPattern;
   174         Formattable timeDatePatterns[] = { fTimePattern, datePattern };
   175         fCombinedFormat->format(timeDatePatterns, 2, combinedPattern, pos, status); // pos is ignored by this
   176         fDateTimeFormatter->applyPattern(combinedPattern);
   177         fDateTimeFormatter->format(cal,appendTo,pos);
   178     }
   180     return appendTo;
   181 }
   185 UnicodeString&
   186 RelativeDateFormat::format(const Formattable& obj, 
   187                          UnicodeString& appendTo, 
   188                          FieldPosition& pos,
   189                          UErrorCode& status) const
   190 {
   191     // this is just here to get around the hiding problem
   192     // (the previous format() override would hide the version of
   193     // format() on DateFormat that this function correspond to, so we
   194     // have to redefine it here)
   195     return DateFormat::format(obj, appendTo, pos, status);
   196 }
   199 void RelativeDateFormat::parse( const UnicodeString& text,
   200                     Calendar& cal,
   201                     ParsePosition& pos) const {
   203     int32_t startIndex = pos.getIndex();
   204     if (fDatePattern.isEmpty()) {
   205         // no date pattern, try parsing as time
   206         fDateTimeFormatter->applyPattern(fTimePattern);
   207         fDateTimeFormatter->parse(text,cal,pos);
   208     } else if (fTimePattern.isEmpty() || fCombinedFormat == NULL) {
   209         // no time pattern or way to combine, try parsing as date
   210         // first check whether text matches a relativeDayString
   211         UBool matchedRelative = FALSE;
   212         for (int n=0; n < fDatesLen && !matchedRelative; n++) {
   213             if (fDates[n].string != NULL &&
   214                     text.compare(startIndex, fDates[n].len, fDates[n].string) == 0) {
   215                 // it matched, handle the relative day string
   216                 UErrorCode status = U_ZERO_ERROR;
   217                 matchedRelative = TRUE;
   219                 // Set the calendar to now+offset
   220                 cal.setTime(Calendar::getNow(),status);
   221                 cal.add(UCAL_DATE,fDates[n].offset, status);
   223                 if(U_FAILURE(status)) { 
   224                     // failure in setting calendar field, set offset to beginning of rel day string
   225                     pos.setErrorIndex(startIndex);
   226                 } else {
   227                     pos.setIndex(startIndex + fDates[n].len);
   228                 }
   229             }
   230         }
   231         if (!matchedRelative) {
   232             // just parse as normal date
   233             fDateTimeFormatter->applyPattern(fDatePattern);
   234             fDateTimeFormatter->parse(text,cal,pos);
   235         }
   236     } else {
   237         // Here we replace any relativeDayString in text with the equivalent date
   238         // formatted per fDatePattern, then parse text normally using the combined pattern.
   239         UnicodeString modifiedText(text);
   240         FieldPosition fPos;
   241         int32_t dateStart = 0, origDateLen = 0, modDateLen = 0;
   242         UErrorCode status = U_ZERO_ERROR;
   243         for (int n=0; n < fDatesLen; n++) {
   244             int32_t relativeStringOffset;
   245             if (fDates[n].string != NULL &&
   246                     (relativeStringOffset = modifiedText.indexOf(fDates[n].string, fDates[n].len, startIndex)) >= startIndex) {
   247                 // it matched, replace the relative date with a real one for parsing
   248                 UnicodeString dateString;
   249                 Calendar * tempCal = cal.clone();
   251                 // Set the calendar to now+offset
   252                 tempCal->setTime(Calendar::getNow(),status);
   253                 tempCal->add(UCAL_DATE,fDates[n].offset, status);
   254                 if(U_FAILURE(status)) { 
   255                     pos.setErrorIndex(startIndex);
   256                     delete tempCal;
   257                     return;
   258                 }
   260                 fDateTimeFormatter->applyPattern(fDatePattern);
   261                 fDateTimeFormatter->format(*tempCal, dateString, fPos);
   262                 dateStart = relativeStringOffset;
   263                 origDateLen = fDates[n].len;
   264                 modDateLen = dateString.length();
   265                 modifiedText.replace(dateStart, origDateLen, dateString);
   266                 delete tempCal;
   267                 break;
   268             }
   269         }
   270         UnicodeString combinedPattern;
   271         Formattable timeDatePatterns[] = { fTimePattern, fDatePattern };
   272         fCombinedFormat->format(timeDatePatterns, 2, combinedPattern, fPos, status); // pos is ignored by this
   273         fDateTimeFormatter->applyPattern(combinedPattern);
   274         fDateTimeFormatter->parse(modifiedText,cal,pos);
   276         // Adjust offsets
   277         UBool noError = (pos.getErrorIndex() < 0);
   278         int32_t offset = (noError)? pos.getIndex(): pos.getErrorIndex();
   279         if (offset >= dateStart + modDateLen) {
   280             // offset at or after the end of the replaced text,
   281             // correct by the difference between original and replacement
   282             offset -= (modDateLen - origDateLen);
   283         } else if (offset >= dateStart) {
   284             // offset in the replaced text, set it to the beginning of that text
   285             // (i.e. the beginning of the relative day string)
   286             offset = dateStart;
   287         }
   288         if (noError) {
   289             pos.setIndex(offset);
   290         } else {
   291             pos.setErrorIndex(offset);
   292         }
   293     }
   294 }
   296 UDate
   297 RelativeDateFormat::parse( const UnicodeString& text,
   298                          ParsePosition& pos) const {
   299     // redefined here because the other parse() function hides this function's
   300     // cunterpart on DateFormat
   301     return DateFormat::parse(text, pos);
   302 }
   304 UDate
   305 RelativeDateFormat::parse(const UnicodeString& text, UErrorCode& status) const
   306 {
   307     // redefined here because the other parse() function hides this function's
   308     // counterpart on DateFormat
   309     return DateFormat::parse(text, status);
   310 }
   313 const UChar *RelativeDateFormat::getStringForDay(int32_t day, int32_t &len, UErrorCode &status) const {
   314     if(U_FAILURE(status)) {
   315         return NULL;
   316     }
   318     // Is it outside the resource bundle's range?
   319     if(day < fDayMin || day > fDayMax) {
   320         return NULL; // don't have it.
   321     }
   323     // Linear search the held strings
   324     for(int n=0;n<fDatesLen;n++) {
   325         if(fDates[n].offset == day) {
   326             len = fDates[n].len;
   327             return fDates[n].string;
   328         }
   329     }
   331     return NULL;  // not found.
   332 }
   334 UnicodeString&
   335 RelativeDateFormat::toPattern(UnicodeString& result, UErrorCode& status) const
   336 {
   337     if (!U_FAILURE(status)) {
   338         result.remove();
   339         if (fDatePattern.isEmpty()) {
   340             result.setTo(fTimePattern);
   341         } else if (fTimePattern.isEmpty() || fCombinedFormat == NULL) {
   342             result.setTo(fDatePattern);
   343         } else {
   344             Formattable timeDatePatterns[] = { fTimePattern, fDatePattern };
   345             FieldPosition pos;
   346             fCombinedFormat->format(timeDatePatterns, 2, result, pos, status);
   347         }
   348     }
   349     return result;
   350 }
   352 UnicodeString&
   353 RelativeDateFormat::toPatternDate(UnicodeString& result, UErrorCode& status) const
   354 {
   355     if (!U_FAILURE(status)) {
   356         result.remove();
   357         result.setTo(fDatePattern);
   358     }
   359     return result;
   360 }
   362 UnicodeString&
   363 RelativeDateFormat::toPatternTime(UnicodeString& result, UErrorCode& status) const
   364 {
   365     if (!U_FAILURE(status)) {
   366         result.remove();
   367         result.setTo(fTimePattern);
   368     }
   369     return result;
   370 }
   372 void
   373 RelativeDateFormat::applyPatterns(const UnicodeString& datePattern, const UnicodeString& timePattern, UErrorCode &status)
   374 {
   375     if (!U_FAILURE(status)) {
   376         fDatePattern.setTo(datePattern);
   377         fTimePattern.setTo(timePattern);
   378     }
   379 }
   381 const DateFormatSymbols*
   382 RelativeDateFormat::getDateFormatSymbols() const
   383 {
   384     return fDateTimeFormatter->getDateFormatSymbols();
   385 }
   387 void RelativeDateFormat::loadDates(UErrorCode &status) {
   388     CalendarData calData(fLocale, "gregorian", status);
   390     UErrorCode tempStatus = status;
   391     UResourceBundle *dateTimePatterns = calData.getByKey(DT_DateTimePatternsTag, tempStatus);
   392     if(U_SUCCESS(tempStatus)) {
   393         int32_t patternsSize = ures_getSize(dateTimePatterns);
   394         if (patternsSize > kDateTime) {
   395             int32_t resStrLen = 0;
   397             int32_t glueIndex = kDateTime;
   398             if (patternsSize >= (DateFormat::kDateTimeOffset + DateFormat::kShort + 1)) {
   399                 // Get proper date time format
   400                 switch (fDateStyle) { 
   401                 case kFullRelative: 
   402                 case kFull: 
   403                     glueIndex = kDateTimeOffset + kFull; 
   404                     break; 
   405                 case kLongRelative: 
   406                 case kLong: 
   407                     glueIndex = kDateTimeOffset + kLong; 
   408                     break; 
   409                 case kMediumRelative: 
   410                 case kMedium: 
   411                     glueIndex = kDateTimeOffset + kMedium; 
   412                     break;         
   413                 case kShortRelative: 
   414                 case kShort: 
   415                     glueIndex = kDateTimeOffset + kShort; 
   416                     break; 
   417                 default: 
   418                     break; 
   419                 } 
   420             }
   422             const UChar *resStr = ures_getStringByIndex(dateTimePatterns, glueIndex, &resStrLen, &tempStatus);
   423             fCombinedFormat = new MessageFormat(UnicodeString(TRUE, resStr, resStrLen), fLocale, tempStatus);
   424         }
   425     }
   427     UResourceBundle *rb = ures_open(NULL, fLocale.getBaseName(), &status);
   428     UResourceBundle *sb = ures_getByKeyWithFallback(rb, "fields", NULL, &status);
   429     rb = ures_getByKeyWithFallback(sb, "day", rb, &status);
   430     sb = ures_getByKeyWithFallback(rb, "relative", sb, &status);
   431     ures_close(rb);
   432     // set up min/max 
   433     fDayMin=-1;
   434     fDayMax=1;
   436     if(U_FAILURE(status)) {
   437         fDatesLen=0;
   438         ures_close(sb);
   439         return;
   440     }
   442     fDatesLen = ures_getSize(sb);
   443     fDates = (URelativeString*) uprv_malloc(sizeof(fDates[0])*fDatesLen);
   445     // Load in each item into the array...
   446     int n = 0;
   448     UResourceBundle *subString = NULL;
   450     while(ures_hasNext(sb) && U_SUCCESS(status)) {  // iterate over items
   451         subString = ures_getNextResource(sb, subString, &status);
   453         if(U_FAILURE(status) || (subString==NULL)) break;
   455         // key = offset #
   456         const char *key = ures_getKey(subString);
   458         // load the string and length
   459         int32_t aLen;
   460         const UChar* aString = ures_getString(subString, &aLen, &status);
   462         if(U_FAILURE(status) || aString == NULL) break;
   464         // calculate the offset
   465         int32_t offset = atoi(key);
   467         // set min/max
   468         if(offset < fDayMin) {
   469             fDayMin = offset;
   470         }
   471         if(offset > fDayMax) {
   472             fDayMax = offset;
   473         }
   475         // copy the string pointer
   476         fDates[n].offset = offset;
   477         fDates[n].string = aString;
   478         fDates[n].len = aLen; 
   480         n++;
   481     }
   482     ures_close(subString);
   483     ures_close(sb);
   485     // the fDates[] array could be sorted here, for direct access.
   486 }
   489 // this should to be in DateFormat, instead it was copied from SimpleDateFormat.
   491 Calendar*
   492 RelativeDateFormat::initializeCalendar(TimeZone* adoptZone, const Locale& locale, UErrorCode& status)
   493 {
   494     if(!U_FAILURE(status)) {
   495         fCalendar = Calendar::createInstance(adoptZone?adoptZone:TimeZone::createDefault(), locale, status);
   496     }
   497     if (U_SUCCESS(status) && fCalendar == NULL) {
   498         status = U_MEMORY_ALLOCATION_ERROR;
   499     }
   500     return fCalendar;
   501 }
   503 int32_t RelativeDateFormat::dayDifference(Calendar &cal, UErrorCode &status) {
   504     if(U_FAILURE(status)) {
   505         return 0;
   506     }
   507     // TODO: Cache the nowCal to avoid heap allocs? Would be difficult, don't know the calendar type
   508     Calendar *nowCal = cal.clone();
   509     nowCal->setTime(Calendar::getNow(), status);
   511     // For the day difference, we are interested in the difference in the (modified) julian day number
   512     // which is midnight to midnight.  Using fieldDifference() is NOT correct here, because 
   513     // 6pm Jan 4th  to 10am Jan 5th should be considered "tomorrow".
   514     int32_t dayDiff = cal.get(UCAL_JULIAN_DAY, status) - nowCal->get(UCAL_JULIAN_DAY, status);
   516     delete nowCal;
   517     return dayDiff;
   518 }
   520 U_NAMESPACE_END
   522 #endif

mercurial