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