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
26U_NAMESPACE_BEGIN
27
28
29/**
30 * An array of URelativeString structs is used to store the resource data loaded out of the bundle.
31 */
32struct 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
38static const char DT_DateTimePatternsTag[]="DateTimePatterns";
39
40
41UOBJECT_DEFINE_RTTI_IMPLEMENTATION(RelativeDateFormat)
42
43RelativeDateFormat::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
72RelativeDateFormat::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
125RelativeDateFormat::~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
135Format* RelativeDateFormat::clone(void) const {
136    return new RelativeDateFormat(*this);
137}
138
139UBool 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
153static const UChar APOSTROPHE = (UChar)0x0027;
154
155UnicodeString& 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
225UnicodeString&
226RelativeDateFormat::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
239void 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
336UDate
337RelativeDateFormat::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
344UDate
345RelativeDateFormat::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
353const 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
374UnicodeString&
375RelativeDateFormat::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
392UnicodeString&
393RelativeDateFormat::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
402UnicodeString&
403RelativeDateFormat::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
412void
413RelativeDateFormat::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
421const DateFormatSymbols*
422RelativeDateFormat::getDateFormatSymbols() const
423{
424    return fDateTimeFormatter->getDateFormatSymbols();
425}
426
427// override the DateFormat implementation in order to
428// lazily initialize relevant items
429void
430RelativeDateFormat::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
454void
455RelativeDateFormat::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
475static const UChar patItem1[] = {0x7B,0x31,0x7D}; // "{1}"
476static const int32_t patItem1Len = 3;
477
478void 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
585Calendar*
586RelativeDateFormat::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
597int32_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
614U_NAMESPACE_END
615
616#endif
617
618