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