OLD | NEW |
(Empty) | |
| 1 /* |
| 2 ******************************************************************************* |
| 3 * Copyright (C) 2007-2010, 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 //#define DEBUG_RELDTFMT |
| 13 |
| 14 #include <stdio.h> |
| 15 #include <stdlib.h> |
| 16 |
| 17 #include "reldtfmt.h" |
| 18 #include "unicode/msgfmt.h" |
| 19 #include "unicode/smpdtfmt.h" |
| 20 |
| 21 #include "gregoimp.h" // for CalendarData |
| 22 #include "cmemory.h" |
| 23 |
| 24 U_NAMESPACE_BEGIN |
| 25 |
| 26 |
| 27 /** |
| 28 * An array of URelativeString structs is used to store the resource data loaded
out of the bundle. |
| 29 */ |
| 30 struct URelativeString { |
| 31 int32_t offset; /** offset of this item, such as, the relative date
**/ |
| 32 int32_t len; /** length of the string **/ |
| 33 const UChar* string; /** string, or NULL if not set **/ |
| 34 }; |
| 35 |
| 36 static const char DT_DateTimePatternsTag[]="DateTimePatterns"; |
| 37 |
| 38 |
| 39 UOBJECT_DEFINE_RTTI_IMPLEMENTATION(RelativeDateFormat) |
| 40 |
| 41 RelativeDateFormat::RelativeDateFormat(const RelativeDateFormat& other) : |
| 42 DateFormat(other), fDateFormat(NULL), fTimeFormat(NULL), fCombinedFormat(NULL), |
| 43 fDateStyle(other.fDateStyle), fTimeStyle(other.fTimeStyle), fLocale(other.fLocal
e), |
| 44 fDayMin(other.fDayMin), fDayMax(other.fDayMax), |
| 45 fDatesLen(other.fDatesLen), fDates(NULL) |
| 46 { |
| 47 if(other.fDateFormat != NULL) { |
| 48 fDateFormat = (DateFormat*)other.fDateFormat->clone(); |
| 49 } else { |
| 50 fDateFormat = NULL; |
| 51 } |
| 52 if (fDatesLen > 0) { |
| 53 fDates = (URelativeString*) uprv_malloc(sizeof(fDates[0])*fDatesLen); |
| 54 uprv_memcpy(fDates, other.fDates, sizeof(fDates[0])*fDatesLen); |
| 55 } |
| 56 //fCalendar = other.fCalendar->clone(); |
| 57 /* |
| 58 if(other.fTimeFormat != NULL) { |
| 59 fTimeFormat = (DateFormat*)other.fTimeFormat->clone(); |
| 60 } else { |
| 61 fTimeFormat = NULL; |
| 62 } |
| 63 */ |
| 64 } |
| 65 |
| 66 RelativeDateFormat::RelativeDateFormat( UDateFormatStyle timeStyle, UDateFormatS
tyle dateStyle, const Locale& locale, UErrorCode& status) |
| 67 : DateFormat(), fDateFormat(NULL), fTimeFormat(NULL), fCombinedFormat(NULL), |
| 68 fDateStyle(dateStyle), fTimeStyle(timeStyle), fLocale(locale), fDatesLen(0), fDa
tes(NULL) |
| 69 { |
| 70 if(U_FAILURE(status) ) { |
| 71 return; |
| 72 } |
| 73 |
| 74 if(fDateStyle != UDAT_NONE) { |
| 75 EStyle newStyle = (EStyle)(fDateStyle & ~UDAT_RELATIVE); |
| 76 // Create a DateFormat in the non-relative style requested. |
| 77 fDateFormat = createDateInstance(newStyle, locale); |
| 78 } |
| 79 if(fTimeStyle >= UDAT_FULL && fTimeStyle <= UDAT_SHORT) { |
| 80 fTimeFormat = createTimeInstance((EStyle)fTimeStyle, locale); |
| 81 } else if(fTimeStyle != UDAT_NONE) { |
| 82 // don't support other time styles (e.g. relative styles), for now |
| 83 status = U_ILLEGAL_ARGUMENT_ERROR; |
| 84 return; |
| 85 } |
| 86 |
| 87 // Initialize the parent fCalendar, so that parse() works correctly. |
| 88 initializeCalendar(NULL, locale, status); |
| 89 loadDates(status); |
| 90 } |
| 91 |
| 92 RelativeDateFormat::~RelativeDateFormat() { |
| 93 delete fDateFormat; |
| 94 delete fTimeFormat; |
| 95 delete fCombinedFormat; |
| 96 uprv_free(fDates); |
| 97 } |
| 98 |
| 99 |
| 100 Format* RelativeDateFormat::clone(void) const { |
| 101 return new RelativeDateFormat(*this); |
| 102 } |
| 103 |
| 104 UBool RelativeDateFormat::operator==(const Format& other) const { |
| 105 if(DateFormat::operator==(other)) { |
| 106 // DateFormat::operator== guarantees following cast is safe |
| 107 RelativeDateFormat* that = (RelativeDateFormat*)&other; |
| 108 return (fDateStyle==that->fDateStyle && |
| 109 fTimeStyle==that->fTimeStyle && |
| 110 fLocale==that->fLocale); |
| 111 } |
| 112 return FALSE; |
| 113 } |
| 114 |
| 115 UnicodeString& RelativeDateFormat::format( Calendar& cal, |
| 116 UnicodeString& appendTo, |
| 117 FieldPosition& pos) const { |
| 118 |
| 119 UErrorCode status = U_ZERO_ERROR; |
| 120 UChar emptyStr = 0; |
| 121 UnicodeString dateString(&emptyStr); |
| 122 |
| 123 // calculate the difference, in days, between 'cal' and now. |
| 124 int dayDiff = dayDifference(cal, status); |
| 125 |
| 126 // look up string |
| 127 int32_t len = 0; |
| 128 const UChar *theString = getStringForDay(dayDiff, len, status); |
| 129 if(U_SUCCESS(status) && (theString!=NULL)) { |
| 130 // found a relative string |
| 131 dateString.setTo(theString, len); |
| 132 } |
| 133 |
| 134 if(fTimeFormat == NULL || fCombinedFormat == 0) { |
| 135 if (dateString.length() > 0) { |
| 136 appendTo.append(dateString); |
| 137 } else if(fDateFormat != NULL) { |
| 138 fDateFormat->format(cal,appendTo,pos); |
| 139 } |
| 140 } else { |
| 141 if (dateString.length() == 0 && fDateFormat != NULL) { |
| 142 fDateFormat->format(cal,dateString,pos); |
| 143 } |
| 144 UnicodeString timeString(&emptyStr); |
| 145 FieldPosition timepos = pos; |
| 146 fTimeFormat->format(cal,timeString,timepos); |
| 147 Formattable timeDateStrings[] = { timeString, dateString }; |
| 148 fCombinedFormat->format(timeDateStrings, 2, appendTo, pos, status); // p
os is ignored by this |
| 149 int32_t offset; |
| 150 if (pos.getEndIndex() > 0 && (offset = appendTo.indexOf(dateString)) >=
0) { |
| 151 // pos.field was found in dateString, offset start & end based on fi
nal position of dateString |
| 152 pos.setBeginIndex( pos.getBeginIndex() + offset ); |
| 153 pos.setEndIndex( pos.getEndIndex() + offset ); |
| 154 } else if (timepos.getEndIndex() > 0 && (offset = appendTo.indexOf(timeS
tring)) >= 0) { |
| 155 // pos.field was found in timeString, offset start & end based on fi
nal position of timeString |
| 156 pos.setBeginIndex( timepos.getBeginIndex() + offset ); |
| 157 pos.setEndIndex( timepos.getEndIndex() + offset ); |
| 158 } |
| 159 } |
| 160 |
| 161 return appendTo; |
| 162 } |
| 163 |
| 164 |
| 165 |
| 166 UnicodeString& |
| 167 RelativeDateFormat::format(const Formattable& obj, |
| 168 UnicodeString& appendTo, |
| 169 FieldPosition& pos, |
| 170 UErrorCode& status) const |
| 171 { |
| 172 // this is just here to get around the hiding problem |
| 173 // (the previous format() override would hide the version of |
| 174 // format() on DateFormat that this function correspond to, so we |
| 175 // have to redefine it here) |
| 176 return DateFormat::format(obj, appendTo, pos, status); |
| 177 } |
| 178 |
| 179 |
| 180 void RelativeDateFormat::parse( const UnicodeString& text, |
| 181 Calendar& cal, |
| 182 ParsePosition& pos) const { |
| 183 |
| 184 // Can the fDateFormat parse it? |
| 185 if(fDateFormat != NULL) { |
| 186 ParsePosition aPos(pos); |
| 187 fDateFormat->parse(text,cal,aPos); |
| 188 if((aPos.getIndex() != pos.getIndex()) && |
| 189 (aPos.getErrorIndex()==-1)) { |
| 190 pos=aPos; // copy the sub parse |
| 191 return; // parsed subfmt OK |
| 192 } |
| 193 } |
| 194 |
| 195 // Linear search the relative strings |
| 196 for(int n=0;n<fDatesLen;n++) { |
| 197 if(fDates[n].string != NULL && |
| 198 (0==text.compare(pos.getIndex(), |
| 199 fDates[n].len, |
| 200 fDates[n].string))) { |
| 201 UErrorCode status = U_ZERO_ERROR; |
| 202 |
| 203 // Set the calendar to now+offset |
| 204 cal.setTime(Calendar::getNow(),status); |
| 205 cal.add(UCAL_DATE,fDates[n].offset, status); |
| 206 |
| 207 if(U_FAILURE(status)) { |
| 208 // failure in setting calendar fields |
| 209 pos.setErrorIndex(pos.getIndex()+fDates[n].len); |
| 210 } else { |
| 211 pos.setIndex(pos.getIndex()+fDates[n].len); |
| 212 } |
| 213 return; |
| 214 } |
| 215 } |
| 216 |
| 217 // parse failed |
| 218 } |
| 219 |
| 220 UDate |
| 221 RelativeDateFormat::parse( const UnicodeString& text, |
| 222 ParsePosition& pos) const { |
| 223 // redefined here because the other parse() function hides this function's |
| 224 // cunterpart on DateFormat |
| 225 return DateFormat::parse(text, pos); |
| 226 } |
| 227 |
| 228 UDate |
| 229 RelativeDateFormat::parse(const UnicodeString& text, UErrorCode& status) const |
| 230 { |
| 231 // redefined here because the other parse() function hides this function's |
| 232 // counterpart on DateFormat |
| 233 return DateFormat::parse(text, status); |
| 234 } |
| 235 |
| 236 |
| 237 const UChar *RelativeDateFormat::getStringForDay(int32_t day, int32_t &len, UErr
orCode &status) const { |
| 238 if(U_FAILURE(status)) { |
| 239 return NULL; |
| 240 } |
| 241 |
| 242 // Is it outside the resource bundle's range? |
| 243 if(day < fDayMin || day > fDayMax) { |
| 244 return NULL; // don't have it. |
| 245 } |
| 246 |
| 247 // Linear search the held strings |
| 248 for(int n=0;n<fDatesLen;n++) { |
| 249 if(fDates[n].offset == day) { |
| 250 len = fDates[n].len; |
| 251 return fDates[n].string; |
| 252 } |
| 253 } |
| 254 |
| 255 return NULL; // not found. |
| 256 } |
| 257 |
| 258 UnicodeString& |
| 259 RelativeDateFormat::toPattern(UnicodeString& result, UErrorCode& status) const |
| 260 { |
| 261 if (!U_FAILURE(status)) { |
| 262 result.remove(); |
| 263 if (fTimeFormat == NULL || fCombinedFormat == 0) { |
| 264 if (fDateFormat != NULL) { |
| 265 UnicodeString datePattern; |
| 266 this->toPatternDate(datePattern, status); |
| 267 if (!U_FAILURE(status)) { |
| 268 result.setTo(datePattern); |
| 269 } |
| 270 } |
| 271 } else { |
| 272 UnicodeString datePattern, timePattern; |
| 273 this->toPatternDate(datePattern, status); |
| 274 this->toPatternTime(timePattern, status); |
| 275 if (!U_FAILURE(status)) { |
| 276 Formattable timeDatePatterns[] = { timePattern, datePattern }; |
| 277 FieldPosition pos; |
| 278 fCombinedFormat->format(timeDatePatterns, 2, result, pos, status
); |
| 279 } |
| 280 } |
| 281 } |
| 282 return result; |
| 283 } |
| 284 |
| 285 UnicodeString& |
| 286 RelativeDateFormat::toPatternDate(UnicodeString& result, UErrorCode& status) con
st |
| 287 { |
| 288 if (!U_FAILURE(status)) { |
| 289 result.remove(); |
| 290 if ( fDateFormat ) { |
| 291 SimpleDateFormat* sdtfmt = dynamic_cast<SimpleDateFormat*>(fDateForm
at); |
| 292 if (sdtfmt != NULL) { |
| 293 sdtfmt->toPattern(result); |
| 294 } else { |
| 295 status = U_UNSUPPORTED_ERROR; |
| 296 } |
| 297 } |
| 298 } |
| 299 return result; |
| 300 } |
| 301 |
| 302 UnicodeString& |
| 303 RelativeDateFormat::toPatternTime(UnicodeString& result, UErrorCode& status) con
st |
| 304 { |
| 305 if (!U_FAILURE(status)) { |
| 306 result.remove(); |
| 307 if ( fTimeFormat ) { |
| 308 SimpleDateFormat* sdtfmt = dynamic_cast<SimpleDateFormat*>(fTimeForm
at); |
| 309 if (sdtfmt != NULL) { |
| 310 sdtfmt->toPattern(result); |
| 311 } else { |
| 312 status = U_UNSUPPORTED_ERROR; |
| 313 } |
| 314 } |
| 315 } |
| 316 return result; |
| 317 } |
| 318 |
| 319 void |
| 320 RelativeDateFormat::applyPatterns(const UnicodeString& datePattern, const Unicod
eString& timePattern, UErrorCode &status) |
| 321 { |
| 322 if (!U_FAILURE(status)) { |
| 323 SimpleDateFormat* sdtfmt = NULL; |
| 324 SimpleDateFormat* stmfmt = NULL; |
| 325 if (fDateFormat && (sdtfmt = dynamic_cast<SimpleDateFormat*>(fDateFormat
)) == NULL) { |
| 326 status = U_UNSUPPORTED_ERROR; |
| 327 return; |
| 328 } |
| 329 if (fTimeFormat && (stmfmt = dynamic_cast<SimpleDateFormat*>(fTimeFormat
)) == NULL) { |
| 330 status = U_UNSUPPORTED_ERROR; |
| 331 return; |
| 332 } |
| 333 if ( fDateFormat ) { |
| 334 sdtfmt->applyPattern(datePattern); |
| 335 } |
| 336 if ( fTimeFormat ) { |
| 337 stmfmt->applyPattern(timePattern); |
| 338 } |
| 339 } |
| 340 } |
| 341 |
| 342 void RelativeDateFormat::loadDates(UErrorCode &status) { |
| 343 CalendarData calData(fLocale, "gregorian", status); |
| 344 |
| 345 UErrorCode tempStatus = status; |
| 346 UResourceBundle *dateTimePatterns = calData.getByKey(DT_DateTimePatternsTag,
tempStatus); |
| 347 if(U_SUCCESS(tempStatus)) { |
| 348 int32_t patternsSize = ures_getSize(dateTimePatterns); |
| 349 if (patternsSize > kDateTime) { |
| 350 int32_t resStrLen = 0; |
| 351 |
| 352 int32_t glueIndex = kDateTime; |
| 353 if (patternsSize >= (DateFormat::kDateTimeOffset + DateFormat::kShor
t + 1)) { |
| 354 // Get proper date time format |
| 355 switch (fDateStyle) { |
| 356 case kFullRelative: |
| 357 case kFull: |
| 358 glueIndex = kDateTimeOffset + kFull; |
| 359 break; |
| 360 case kLongRelative: |
| 361 case kLong: |
| 362 glueIndex = kDateTimeOffset + kLong; |
| 363 break; |
| 364 case kMediumRelative: |
| 365 case kMedium: |
| 366 glueIndex = kDateTimeOffset + kMedium; |
| 367 break; |
| 368 case kShortRelative: |
| 369 case kShort: |
| 370 glueIndex = kDateTimeOffset + kShort; |
| 371 break; |
| 372 default: |
| 373 break; |
| 374 } |
| 375 } |
| 376 |
| 377 const UChar *resStr = ures_getStringByIndex(dateTimePatterns, glueIn
dex, &resStrLen, &tempStatus); |
| 378 fCombinedFormat = new MessageFormat(UnicodeString(TRUE, resStr, resS
trLen), fLocale, tempStatus); |
| 379 } |
| 380 } |
| 381 |
| 382 UResourceBundle *strings = calData.getByKey3("fields", "day", "relative", st
atus); |
| 383 // set up min/max |
| 384 fDayMin=-1; |
| 385 fDayMax=1; |
| 386 |
| 387 if(U_FAILURE(status)) { |
| 388 fDatesLen=0; |
| 389 return; |
| 390 } |
| 391 |
| 392 fDatesLen = ures_getSize(strings); |
| 393 fDates = (URelativeString*) uprv_malloc(sizeof(fDates[0])*fDatesLen); |
| 394 |
| 395 // Load in each item into the array... |
| 396 int n = 0; |
| 397 |
| 398 UResourceBundle *subString = NULL; |
| 399 |
| 400 while(ures_hasNext(strings) && U_SUCCESS(status)) { // iterate over items |
| 401 subString = ures_getNextResource(strings, subString, &status); |
| 402 |
| 403 if(U_FAILURE(status) || (subString==NULL)) break; |
| 404 |
| 405 // key = offset # |
| 406 const char *key = ures_getKey(subString); |
| 407 |
| 408 // load the string and length |
| 409 int32_t aLen; |
| 410 const UChar* aString = ures_getString(subString, &aLen, &status); |
| 411 |
| 412 if(U_FAILURE(status) || aString == NULL) break; |
| 413 |
| 414 // calculate the offset |
| 415 int32_t offset = atoi(key); |
| 416 |
| 417 // set min/max |
| 418 if(offset < fDayMin) { |
| 419 fDayMin = offset; |
| 420 } |
| 421 if(offset > fDayMax) { |
| 422 fDayMax = offset; |
| 423 } |
| 424 |
| 425 // copy the string pointer |
| 426 fDates[n].offset = offset; |
| 427 fDates[n].string = aString; |
| 428 fDates[n].len = aLen; |
| 429 |
| 430 n++; |
| 431 } |
| 432 ures_close(subString); |
| 433 |
| 434 // the fDates[] array could be sorted here, for direct access. |
| 435 } |
| 436 |
| 437 |
| 438 // this should to be in DateFormat, instead it was copied from SimpleDateFormat. |
| 439 |
| 440 Calendar* |
| 441 RelativeDateFormat::initializeCalendar(TimeZone* adoptZone, const Locale& locale
, UErrorCode& status) |
| 442 { |
| 443 if(!U_FAILURE(status)) { |
| 444 fCalendar = Calendar::createInstance(adoptZone?adoptZone:TimeZone::creat
eDefault(), locale, status); |
| 445 } |
| 446 if (U_SUCCESS(status) && fCalendar == NULL) { |
| 447 status = U_MEMORY_ALLOCATION_ERROR; |
| 448 } |
| 449 return fCalendar; |
| 450 } |
| 451 |
| 452 int32_t RelativeDateFormat::dayDifference(Calendar &cal, UErrorCode &status) { |
| 453 if(U_FAILURE(status)) { |
| 454 return 0; |
| 455 } |
| 456 // TODO: Cache the nowCal to avoid heap allocs? Would be difficult, don't kn
ow the calendar type |
| 457 Calendar *nowCal = cal.clone(); |
| 458 nowCal->setTime(Calendar::getNow(), status); |
| 459 |
| 460 // For the day difference, we are interested in the difference in the (modif
ied) julian day number |
| 461 // which is midnight to midnight. Using fieldDifference() is NOT correct he
re, because |
| 462 // 6pm Jan 4th to 10am Jan 5th should be considered "tomorrow". |
| 463 int32_t dayDiff = cal.get(UCAL_JULIAN_DAY, status) - nowCal->get(UCAL_JULIAN
_DAY, status); |
| 464 |
| 465 delete nowCal; |
| 466 return dayDiff; |
| 467 } |
| 468 |
| 469 U_NAMESPACE_END |
| 470 |
| 471 #endif |
| 472 |
OLD | NEW |