Imported Upstream version 58.1
[platform/upstream/icu.git] / source / i18n / reldtfmt.cpp
1 // Copyright (C) 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
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 UChar* string;    /** string, or NULL if not set **/
38 };
39
40 UOBJECT_DEFINE_RTTI_IMPLEMENTATION(RelativeDateFormat)
41
42 RelativeDateFormat::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
70 RelativeDateFormat::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
124 RelativeDateFormat::~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
134 Format* RelativeDateFormat::clone(void) const {
135     return new RelativeDateFormat(*this);
136 }
137
138 UBool 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
152 static const UChar APOSTROPHE = (UChar)0x0027;
153
154 UnicodeString& 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
223 UnicodeString&
224 RelativeDateFormat::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
237 void 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
333 UDate
334 RelativeDateFormat::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
341 UDate
342 RelativeDateFormat::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
350 const 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
366 UnicodeString&
367 RelativeDateFormat::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
382 UnicodeString&
383 RelativeDateFormat::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
392 UnicodeString&
393 RelativeDateFormat::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
402 void
403 RelativeDateFormat::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
411 const DateFormatSymbols*
412 RelativeDateFormat::getDateFormatSymbols() const
413 {
414     return fDateTimeFormatter->getDateFormatSymbols();
415 }
416
417 // override the DateFormat implementation in order to
418 // lazily initialize relevant items
419 void
420 RelativeDateFormat::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
444 void
445 RelativeDateFormat::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
466 namespace {
467
468 /**
469  * Sink for getting data from fields/day/relative data.
470  * For loading relative day names, e.g., "yesterday", "today".
471  */
472
473 struct 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.
510 RelDateFmtDataSink::~RelDateFmtDataSink() {}
511
512 }  // Namespace
513
514
515 static const UChar patItem1[] = {0x7B,0x31,0x7D}; // "{1}"
516 static const int32_t patItem1Len = 3;
517
518 void 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
564 Calendar*
565 RelativeDateFormat::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
576 int32_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
593 U_NAMESPACE_END
594
595 #endif  /* !UCONFIG_NO_FORMATTING */