1 /* 2 ******************************************************************************* 3 * Copyright (C) 2007-2009, 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.fLocale), 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, UDateFormatStyle dateStyle, const Locale& locale, UErrorCode& status) 67 : DateFormat(), fDateFormat(NULL), fTimeFormat(NULL), fCombinedFormat(NULL), 68 fDateStyle(dateStyle), fTimeStyle(timeStyle), fLocale(locale), fDatesLen(0), fDates(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; 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); // pos 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 final position of dateString 152 pos.setBeginIndex( pos.getBeginIndex() + offset ); 153 pos.setEndIndex( pos.getEndIndex() + offset ); 154 } else if (timepos.getEndIndex() > 0 && (offset = appendTo.indexOf(timeString)) >= 0) { 155 // pos.field was found in timeString, offset start & end based on final 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, UErrorCode &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) const 287 { 288 if (!U_FAILURE(status)) { 289 result.remove(); 290 if ( fDateFormat ) { 291 if ( fDateFormat->getDynamicClassID()==SimpleDateFormat::getStaticClassID() ) { 292 ((SimpleDateFormat*)fDateFormat)->toPattern(result); 293 } else { 294 status = U_UNSUPPORTED_ERROR; 295 } 296 } 297 } 298 return result; 299 } 300 301 UnicodeString& 302 RelativeDateFormat::toPatternTime(UnicodeString& result, UErrorCode& status) const 303 { 304 if (!U_FAILURE(status)) { 305 result.remove(); 306 if ( fTimeFormat ) { 307 if ( fTimeFormat->getDynamicClassID()==SimpleDateFormat::getStaticClassID() ) { 308 ((SimpleDateFormat*)fTimeFormat)->toPattern(result); 309 } else { 310 status = U_UNSUPPORTED_ERROR; 311 } 312 } 313 } 314 return result; 315 } 316 317 void 318 RelativeDateFormat::applyPatterns(const UnicodeString& datePattern, const UnicodeString& timePattern, UErrorCode &status) 319 { 320 if (!U_FAILURE(status)) { 321 if ( fDateFormat && fDateFormat->getDynamicClassID()!=SimpleDateFormat::getStaticClassID() ) { 322 status = U_UNSUPPORTED_ERROR; 323 return; 324 } 325 if ( fTimeFormat && fTimeFormat->getDynamicClassID()!=SimpleDateFormat::getStaticClassID() ) { 326 status = U_UNSUPPORTED_ERROR; 327 return; 328 } 329 if ( fDateFormat ) { 330 ((SimpleDateFormat*)fDateFormat)->applyPattern(datePattern); 331 } 332 if ( fTimeFormat ) { 333 ((SimpleDateFormat*)fTimeFormat)->applyPattern(timePattern); 334 } 335 } 336 } 337 338 void RelativeDateFormat::loadDates(UErrorCode &status) { 339 CalendarData calData(fLocale, "gregorian", status); 340 341 UErrorCode tempStatus = status; 342 UResourceBundle *dateTimePatterns = calData.getByKey(DT_DateTimePatternsTag, tempStatus); 343 if(U_SUCCESS(tempStatus)) { 344 int32_t patternsSize = ures_getSize(dateTimePatterns); 345 if (patternsSize > kDateTime) { 346 int32_t resStrLen = 0; 347 348 int32_t glueIndex = kDateTime; 349 if (patternsSize >= (DateFormat::kDateTimeOffset + DateFormat::kShort + 1)) { 350 // Get proper date time format 351 switch (fDateStyle) { 352 case kFullRelative: 353 case kFull: 354 glueIndex = kDateTimeOffset + kFull; 355 break; 356 case kLongRelative: 357 case kLong: 358 glueIndex = kDateTimeOffset + kLong; 359 break; 360 case kMediumRelative: 361 case kMedium: 362 glueIndex = kDateTimeOffset + kMedium; 363 break; 364 case kShortRelative: 365 case kShort: 366 glueIndex = kDateTimeOffset + kShort; 367 break; 368 default: 369 break; 370 } 371 } 372 373 const UChar *resStr = ures_getStringByIndex(dateTimePatterns, glueIndex, &resStrLen, &tempStatus); 374 fCombinedFormat = new MessageFormat(UnicodeString(TRUE, resStr, resStrLen), fLocale, tempStatus); 375 } 376 } 377 378 UResourceBundle *strings = calData.getByKey3("fields", "day", "relative", status); 379 // set up min/max 380 fDayMin=-1; 381 fDayMax=1; 382 383 if(U_FAILURE(status)) { 384 fDatesLen=0; 385 return; 386 } 387 388 fDatesLen = ures_getSize(strings); 389 fDates = (URelativeString*) uprv_malloc(sizeof(fDates[0])*fDatesLen); 390 391 // Load in each item into the array... 392 int n = 0; 393 394 UResourceBundle *subString = NULL; 395 396 while(ures_hasNext(strings) && U_SUCCESS(status)) { // iterate over items 397 subString = ures_getNextResource(strings, subString, &status); 398 399 if(U_FAILURE(status) || (subString==NULL)) break; 400 401 // key = offset # 402 const char *key = ures_getKey(subString); 403 404 // load the string and length 405 int32_t aLen; 406 const UChar* aString = ures_getString(subString, &aLen, &status); 407 408 if(U_FAILURE(status) || aString == NULL) break; 409 410 // calculate the offset 411 int32_t offset = atoi(key); 412 413 // set min/max 414 if(offset < fDayMin) { 415 fDayMin = offset; 416 } 417 if(offset > fDayMax) { 418 fDayMax = offset; 419 } 420 421 // copy the string pointer 422 fDates[n].offset = offset; 423 fDates[n].string = aString; 424 fDates[n].len = aLen; 425 426 n++; 427 } 428 ures_close(subString); 429 430 // the fDates[] array could be sorted here, for direct access. 431 } 432 433 434 // this should to be in DateFormat, instead it was copied from SimpleDateFormat. 435 436 Calendar* 437 RelativeDateFormat::initializeCalendar(TimeZone* adoptZone, const Locale& locale, UErrorCode& status) 438 { 439 if(!U_FAILURE(status)) { 440 fCalendar = Calendar::createInstance(adoptZone?adoptZone:TimeZone::createDefault(), locale, status); 441 } 442 if (U_SUCCESS(status) && fCalendar == NULL) { 443 status = U_MEMORY_ALLOCATION_ERROR; 444 } 445 return fCalendar; 446 } 447 448 int32_t RelativeDateFormat::dayDifference(Calendar &cal, UErrorCode &status) { 449 if(U_FAILURE(status)) { 450 return 0; 451 } 452 // TODO: Cache the nowCal to avoid heap allocs? Would be difficult, don't know the calendar type 453 Calendar *nowCal = cal.clone(); 454 nowCal->setTime(Calendar::getNow(), status); 455 456 // For the day difference, we are interested in the difference in the (modified) julian day number 457 // which is midnight to midnight. Using fieldDifference() is NOT correct here, because 458 // 6pm Jan 4th to 10am Jan 5th should be considered "tomorrow". 459 int32_t dayDiff = cal.get(UCAL_JULIAN_DAY, status) - nowCal->get(UCAL_JULIAN_DAY, status); 460 461 delete nowCal; 462 return dayDiff; 463 } 464 465 U_NAMESPACE_END 466 467 #endif 468 469