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