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
UOBJECT_DEFINE_RTTI_IMPLEMENTATION(RelativeDateFormat)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 = (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 != nullptr) {
65 fCapitalizationBrkIter = (other.fCapitalizationBrkIter)->clone();
66 }
67 #endif
68 }
69
RelativeDateFormat(UDateFormatStyle timeStyle,UDateFormatStyle dateStyle,const Locale & locale,UErrorCode & status)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)? (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((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((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((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
~RelativeDateFormat()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
clone() const142 RelativeDateFormat* RelativeDateFormat::clone() const {
143 return new RelativeDateFormat(*this);
144 }
145
operator ==(const Format & other) const146 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 = (char16_t)0x0027;
161
format(Calendar & cal,UnicodeString & appendTo,FieldPosition & pos) const162 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&
format(const Formattable & obj,UnicodeString & appendTo,FieldPosition & pos,UErrorCode & status) const232 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
parse(const UnicodeString & text,Calendar & cal,ParsePosition & pos) const245 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
parse(const UnicodeString & text,ParsePosition & pos) const342 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
parse(const UnicodeString & text,UErrorCode & status) const350 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
getStringForDay(int32_t day,int32_t & len,UErrorCode & status) const358 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&
toPattern(UnicodeString & result,UErrorCode & status) const375 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&
toPatternDate(UnicodeString & result,UErrorCode & status) const391 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&
toPatternTime(UnicodeString & result,UErrorCode & status) const401 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
applyPatterns(const UnicodeString & datePattern,const UnicodeString & timePattern,UErrorCode & status)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*
getDateFormatSymbols() const420 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
setContext(UDisplayContext value,UErrorCode & status)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
initCapitalizationContextInfo(const Locale & thelocale)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
RelDateFmtDataSink__anonbb19b7ec0111::RelDateFmtDataSink485 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
put__anonbb19b7ec0111::RelDateFmtDataSink495 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 (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.
~RelDateFmtDataSink()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
loadDates(UErrorCode & status)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 >= (int32_t)kFull &&
540 offsetIncrement <= (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 = (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*
initializeCalendar(TimeZone * adoptZone,const Locale & locale,UErrorCode & status)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
dayDifference(Calendar & cal,UErrorCode & status)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 */
604