1e3a051e0538a605551f4d58294c94f5eb00ed07fKeith Whitwell/*
2e3a051e0538a605551f4d58294c94f5eb00ed07fKeith Whitwell*******************************************************************************
3efda5cb6263631175aa2efe46df9322b3c5775eeBrian* Copyright (C) 2007-2014, International Business Machines Corporation and
422144ab7552f0799bcfca506bf4ffa7f70a06649Gareth Hughes* others. All Rights Reserved.
59ede048127ea71282fd97e01516dedcfb03e2a23Brian*******************************************************************************
622144ab7552f0799bcfca506bf4ffa7f70a06649Gareth Hughes*/
7e3a051e0538a605551f4d58294c94f5eb00ed07fKeith Whitwell
8e3a051e0538a605551f4d58294c94f5eb00ed07fKeith Whitwell#include "unicode/utypes.h"
9e3a051e0538a605551f4d58294c94f5eb00ed07fKeith Whitwell
10e3a051e0538a605551f4d58294c94f5eb00ed07fKeith Whitwell#if !UCONFIG_NO_FORMATTING
11e3a051e0538a605551f4d58294c94f5eb00ed07fKeith Whitwell
12e3a051e0538a605551f4d58294c94f5eb00ed07fKeith Whitwell#include <stdlib.h>
1322144ab7552f0799bcfca506bf4ffa7f70a06649Gareth Hughes
14e3a051e0538a605551f4d58294c94f5eb00ed07fKeith Whitwell#include "reldtfmt.h"
15e3a051e0538a605551f4d58294c94f5eb00ed07fKeith Whitwell#include "unicode/datefmt.h"
1622144ab7552f0799bcfca506bf4ffa7f70a06649Gareth Hughes#include "unicode/smpdtfmt.h"
17e3a051e0538a605551f4d58294c94f5eb00ed07fKeith Whitwell#include "unicode/msgfmt.h"
18e3a051e0538a605551f4d58294c94f5eb00ed07fKeith Whitwell#include "unicode/udisplaycontext.h"
19e3a051e0538a605551f4d58294c94f5eb00ed07fKeith Whitwell#include "unicode/uchar.h"
20e3a051e0538a605551f4d58294c94f5eb00ed07fKeith Whitwell#include "unicode/brkiter.h"
21e3a051e0538a605551f4d58294c94f5eb00ed07fKeith Whitwell
22e3a051e0538a605551f4d58294c94f5eb00ed07fKeith Whitwell#include "gregoimp.h" // for CalendarData
23e3a051e0538a605551f4d58294c94f5eb00ed07fKeith Whitwell#include "cmemory.h"
24e3a051e0538a605551f4d58294c94f5eb00ed07fKeith Whitwell#include "uresimp.h"
25e3a051e0538a605551f4d58294c94f5eb00ed07fKeith Whitwell
26bbd287103dad776d8a45c87c4e51fbc26d9b80d5Brian PaulU_NAMESPACE_BEGIN
27bbd287103dad776d8a45c87c4e51fbc26d9b80d5Brian Paul
28bbd287103dad776d8a45c87c4e51fbc26d9b80d5Brian Paul
29bbd287103dad776d8a45c87c4e51fbc26d9b80d5Brian Paul/**
300070d398d13759adc519f9bc764ffd39bc88890eBrian Paul * An array of URelativeString structs is used to store the resource data loaded out of the bundle.
31cd03ed4f54444d96e4e47cdb118a3dfd94d92bb0Keith Whitwell */
32cd03ed4f54444d96e4e47cdb118a3dfd94d92bb0Keith Whitwellstruct URelativeString {
33b37a084357dd08573b86d6d8c5ba43d65bdc1bd7Brian Paul    int32_t offset;         /** offset of this item, such as, the relative date **/
34b37a084357dd08573b86d6d8c5ba43d65bdc1bd7Brian Paul    int32_t len;            /** length of the string **/
35e3a051e0538a605551f4d58294c94f5eb00ed07fKeith Whitwell    const UChar* string;    /** string, or NULL if not set **/
36e3a051e0538a605551f4d58294c94f5eb00ed07fKeith Whitwell};
37b37a084357dd08573b86d6d8c5ba43d65bdc1bd7Brian Paul
38b37a084357dd08573b86d6d8c5ba43d65bdc1bd7Brian Paulstatic const char DT_DateTimePatternsTag[]="DateTimePatterns";
39b37a084357dd08573b86d6d8c5ba43d65bdc1bd7Brian Paul
40b37a084357dd08573b86d6d8c5ba43d65bdc1bd7Brian Paul
41f9995b30756140724f41daf963fa06167912be7fKristian HøgsbergUOBJECT_DEFINE_RTTI_IMPLEMENTATION(RelativeDateFormat)
42b37a084357dd08573b86d6d8c5ba43d65bdc1bd7Brian Paul
43b37a084357dd08573b86d6d8c5ba43d65bdc1bd7Brian PaulRelativeDateFormat::RelativeDateFormat(const RelativeDateFormat& other) :
44b37a084357dd08573b86d6d8c5ba43d65bdc1bd7Brian Paul DateFormat(other), fDateTimeFormatter(NULL), fDatePattern(other.fDatePattern),
45e3a051e0538a605551f4d58294c94f5eb00ed07fKeith Whitwell fTimePattern(other.fTimePattern), fCombinedFormat(NULL),
46b37a084357dd08573b86d6d8c5ba43d65bdc1bd7Brian Paul fDateStyle(other.fDateStyle), fLocale(other.fLocale),
47b37a084357dd08573b86d6d8c5ba43d65bdc1bd7Brian Paul fDayMin(other.fDayMin), fDayMax(other.fDayMax),
48b37a084357dd08573b86d6d8c5ba43d65bdc1bd7Brian Paul fDatesLen(other.fDatesLen), fDates(NULL),
49b37a084357dd08573b86d6d8c5ba43d65bdc1bd7Brian Paul fCombinedHasDateAtStart(other.fCombinedHasDateAtStart),
50b37a084357dd08573b86d6d8c5ba43d65bdc1bd7Brian Paul fCapitalizationInfoSet(other.fCapitalizationInfoSet),
51b37a084357dd08573b86d6d8c5ba43d65bdc1bd7Brian Paul fCapitalizationOfRelativeUnitsForUIListMenu(other.fCapitalizationOfRelativeUnitsForUIListMenu),
52b37a084357dd08573b86d6d8c5ba43d65bdc1bd7Brian Paul fCapitalizationOfRelativeUnitsForStandAlone(other.fCapitalizationOfRelativeUnitsForStandAlone),
53b37a084357dd08573b86d6d8c5ba43d65bdc1bd7Brian Paul fCapitalizationBrkIter(NULL)
54b37a084357dd08573b86d6d8c5ba43d65bdc1bd7Brian Paul{
55b37a084357dd08573b86d6d8c5ba43d65bdc1bd7Brian Paul    if(other.fDateTimeFormatter != NULL) {
56b37a084357dd08573b86d6d8c5ba43d65bdc1bd7Brian Paul        fDateTimeFormatter = (SimpleDateFormat*)other.fDateTimeFormatter->clone();
57e3a051e0538a605551f4d58294c94f5eb00ed07fKeith Whitwell    }
58e3a051e0538a605551f4d58294c94f5eb00ed07fKeith Whitwell    if(other.fCombinedFormat != NULL) {
59e3a051e0538a605551f4d58294c94f5eb00ed07fKeith Whitwell        fCombinedFormat = (MessageFormat*)other.fCombinedFormat->clone();
60b37a084357dd08573b86d6d8c5ba43d65bdc1bd7Brian Paul    }
61e3a051e0538a605551f4d58294c94f5eb00ed07fKeith Whitwell    if (fDatesLen > 0) {
62b37a084357dd08573b86d6d8c5ba43d65bdc1bd7Brian Paul        fDates = (URelativeString*) uprv_malloc(sizeof(fDates[0])*fDatesLen);
63f9995b30756140724f41daf963fa06167912be7fKristian Høgsberg        uprv_memcpy(fDates, other.fDates, sizeof(fDates[0])*fDatesLen);
64e3a051e0538a605551f4d58294c94f5eb00ed07fKeith Whitwell    }
65af2aa8e9cf88a9ee3ec338eddc9a47bf2f142cb7Brian#if !UCONFIG_NO_BREAK_ITERATION
66af2aa8e9cf88a9ee3ec338eddc9a47bf2f142cb7Brian    if (other.fCapitalizationBrkIter != NULL) {
67af2aa8e9cf88a9ee3ec338eddc9a47bf2f142cb7Brian        fCapitalizationBrkIter = (other.fCapitalizationBrkIter)->clone();
68af2aa8e9cf88a9ee3ec338eddc9a47bf2f142cb7Brian    }
69cd03ed4f54444d96e4e47cdb118a3dfd94d92bb0Keith Whitwell#endif
70b37a084357dd08573b86d6d8c5ba43d65bdc1bd7Brian Paul}
71e3a051e0538a605551f4d58294c94f5eb00ed07fKeith Whitwell
72b37a084357dd08573b86d6d8c5ba43d65bdc1bd7Brian PaulRelativeDateFormat::RelativeDateFormat( UDateFormatStyle timeStyle, UDateFormatStyle dateStyle,
73b37a084357dd08573b86d6d8c5ba43d65bdc1bd7Brian Paul                                        const Locale& locale, UErrorCode& status) :
74b37a084357dd08573b86d6d8c5ba43d65bdc1bd7Brian Paul DateFormat(), fDateTimeFormatter(NULL), fDatePattern(), fTimePattern(), fCombinedFormat(NULL),
75b37a084357dd08573b86d6d8c5ba43d65bdc1bd7Brian Paul fDateStyle(dateStyle), fLocale(locale), fDayMin(0), fDayMax(0), fDatesLen(0), fDates(NULL),
76b37a084357dd08573b86d6d8c5ba43d65bdc1bd7Brian Paul fCombinedHasDateAtStart(FALSE), fCapitalizationInfoSet(FALSE),
77b37a084357dd08573b86d6d8c5ba43d65bdc1bd7Brian Paul fCapitalizationOfRelativeUnitsForUIListMenu(FALSE), fCapitalizationOfRelativeUnitsForStandAlone(FALSE),
7877df88727cb0a423dd5cb41498c2302d9df4fce7Brian Paul fCapitalizationBrkIter(NULL)
79a670c1280b78e6da3b298b61f623e4c733c6be94Brian Paul{
80a670c1280b78e6da3b298b61f623e4c733c6be94Brian Paul    if(U_FAILURE(status) ) {
81b37a084357dd08573b86d6d8c5ba43d65bdc1bd7Brian Paul        return;
82b37a084357dd08573b86d6d8c5ba43d65bdc1bd7Brian Paul    }
83b37a084357dd08573b86d6d8c5ba43d65bdc1bd7Brian Paul
8477df88727cb0a423dd5cb41498c2302d9df4fce7Brian Paul    if (timeStyle < UDAT_NONE || timeStyle > UDAT_SHORT) {
85b37a084357dd08573b86d6d8c5ba43d65bdc1bd7Brian Paul        // don't support other time styles (e.g. relative styles), for now
86b37a084357dd08573b86d6d8c5ba43d65bdc1bd7Brian Paul        status = U_ILLEGAL_ARGUMENT_ERROR;
87b37a084357dd08573b86d6d8c5ba43d65bdc1bd7Brian Paul        return;
8877df88727cb0a423dd5cb41498c2302d9df4fce7Brian Paul    }
89b37a084357dd08573b86d6d8c5ba43d65bdc1bd7Brian Paul    UDateFormatStyle baseDateStyle = (dateStyle > UDAT_SHORT)? (UDateFormatStyle)(dateStyle & ~UDAT_RELATIVE): dateStyle;
90e6df80184b0c7e83eba382b3161ba11db497c55dIan Romanick    DateFormat * df;
91b37a084357dd08573b86d6d8c5ba43d65bdc1bd7Brian Paul    // Get fDateTimeFormatter from either date or time style (does not matter, we will override the pattern).
92b37a084357dd08573b86d6d8c5ba43d65bdc1bd7Brian Paul    // We do need to get separate patterns for the date & time styles.
93b37a084357dd08573b86d6d8c5ba43d65bdc1bd7Brian Paul    if (baseDateStyle != UDAT_NONE) {
9477df88727cb0a423dd5cb41498c2302d9df4fce7Brian Paul        df = createDateInstance((EStyle)baseDateStyle, locale);
95a670c1280b78e6da3b298b61f623e4c733c6be94Brian Paul        fDateTimeFormatter=dynamic_cast<SimpleDateFormat *>(df);
96a670c1280b78e6da3b298b61f623e4c733c6be94Brian Paul        if (fDateTimeFormatter == NULL) {
97b37a084357dd08573b86d6d8c5ba43d65bdc1bd7Brian Paul            status = U_UNSUPPORTED_ERROR;
98b37a084357dd08573b86d6d8c5ba43d65bdc1bd7Brian Paul             return;
99b37a084357dd08573b86d6d8c5ba43d65bdc1bd7Brian Paul        }
10077df88727cb0a423dd5cb41498c2302d9df4fce7Brian Paul        fDateTimeFormatter->toPattern(fDatePattern);
101b37a084357dd08573b86d6d8c5ba43d65bdc1bd7Brian Paul        if (timeStyle != UDAT_NONE) {
102b37a084357dd08573b86d6d8c5ba43d65bdc1bd7Brian Paul            df = createTimeInstance((EStyle)timeStyle, locale);
103b37a084357dd08573b86d6d8c5ba43d65bdc1bd7Brian Paul            SimpleDateFormat *sdf = dynamic_cast<SimpleDateFormat *>(df);
10477df88727cb0a423dd5cb41498c2302d9df4fce7Brian Paul            if (sdf != NULL) {
105b37a084357dd08573b86d6d8c5ba43d65bdc1bd7Brian Paul                sdf->toPattern(fTimePattern);
106e6df80184b0c7e83eba382b3161ba11db497c55dIan Romanick                delete sdf;
107b37a084357dd08573b86d6d8c5ba43d65bdc1bd7Brian Paul            }
108b37a084357dd08573b86d6d8c5ba43d65bdc1bd7Brian Paul        }
109e3a051e0538a605551f4d58294c94f5eb00ed07fKeith Whitwell    } else {
110e3a051e0538a605551f4d58294c94f5eb00ed07fKeith Whitwell        // does not matter whether timeStyle is UDAT_NONE, we need something for fDateTimeFormatter
111e3a051e0538a605551f4d58294c94f5eb00ed07fKeith Whitwell        df = createTimeInstance((EStyle)timeStyle, locale);
112e3a051e0538a605551f4d58294c94f5eb00ed07fKeith Whitwell        fDateTimeFormatter=dynamic_cast<SimpleDateFormat *>(df);
113b37a084357dd08573b86d6d8c5ba43d65bdc1bd7Brian Paul        if (fDateTimeFormatter == NULL) {
114b37a084357dd08573b86d6d8c5ba43d65bdc1bd7Brian Paul            status = U_UNSUPPORTED_ERROR;
115b37a084357dd08573b86d6d8c5ba43d65bdc1bd7Brian Paul            return;
116b37a084357dd08573b86d6d8c5ba43d65bdc1bd7Brian Paul        }
11722a47c5251ee7b91dc8f7f4f7dbeb3ad5a117b70Brian Paul        fDateTimeFormatter->toPattern(fTimePattern);
1189e8a961dd7d7b717a9fb4ecdea1c1b60ea355efeBrian    }
11922a47c5251ee7b91dc8f7f4f7dbeb3ad5a117b70Brian Paul
12045bc887da226403f2c41077e40ca38b6f60f1359Brian Paul    // Initialize the parent fCalendar, so that parse() works correctly.
121e3a051e0538a605551f4d58294c94f5eb00ed07fKeith Whitwell    initializeCalendar(NULL, locale, status);
122e3a051e0538a605551f4d58294c94f5eb00ed07fKeith Whitwell    loadDates(status);
123e3a051e0538a605551f4d58294c94f5eb00ed07fKeith Whitwell}
12422a47c5251ee7b91dc8f7f4f7dbeb3ad5a117b70Brian Paul
1259e8a961dd7d7b717a9fb4ecdea1c1b60ea355efeBrianRelativeDateFormat::~RelativeDateFormat() {
12622a47c5251ee7b91dc8f7f4f7dbeb3ad5a117b70Brian Paul    delete fDateTimeFormatter;
12722a47c5251ee7b91dc8f7f4f7dbeb3ad5a117b70Brian Paul    delete fCombinedFormat;
12822a47c5251ee7b91dc8f7f4f7dbeb3ad5a117b70Brian Paul    uprv_free(fDates);
12922a47c5251ee7b91dc8f7f4f7dbeb3ad5a117b70Brian Paul#if !UCONFIG_NO_BREAK_ITERATION
13022a47c5251ee7b91dc8f7f4f7dbeb3ad5a117b70Brian Paul    delete fCapitalizationBrkIter;
13122a47c5251ee7b91dc8f7f4f7dbeb3ad5a117b70Brian Paul#endif
13222a47c5251ee7b91dc8f7f4f7dbeb3ad5a117b70Brian Paul}
133af2aa8e9cf88a9ee3ec338eddc9a47bf2f142cb7Brian
134dc24230de7f913969b52dee3579bb8fa3d50a8c0Karl Schultz
13522a47c5251ee7b91dc8f7f4f7dbeb3ad5a117b70Brian PaulFormat* RelativeDateFormat::clone(void) const {
13622a47c5251ee7b91dc8f7f4f7dbeb3ad5a117b70Brian Paul    return new RelativeDateFormat(*this);
13745bc887da226403f2c41077e40ca38b6f60f1359Brian Paul}
138e3a051e0538a605551f4d58294c94f5eb00ed07fKeith Whitwell
139e3a051e0538a605551f4d58294c94f5eb00ed07fKeith WhitwellUBool RelativeDateFormat::operator==(const Format& other) const {
140b37a084357dd08573b86d6d8c5ba43d65bdc1bd7Brian Paul    if(DateFormat::operator==(other)) {
141e3a051e0538a605551f4d58294c94f5eb00ed07fKeith Whitwell        // The DateFormat::operator== check for fCapitalizationContext equality above
1429e8a961dd7d7b717a9fb4ecdea1c1b60ea355efeBrian        //   is sufficient to check equality of all derived context-related data.
1439e8a961dd7d7b717a9fb4ecdea1c1b60ea355efeBrian        // DateFormat::operator== guarantees following cast is safe
14422a47c5251ee7b91dc8f7f4f7dbeb3ad5a117b70Brian Paul        RelativeDateFormat* that = (RelativeDateFormat*)&other;
14522a47c5251ee7b91dc8f7f4f7dbeb3ad5a117b70Brian Paul        return (fDateStyle==that->fDateStyle   &&
1469ede048127ea71282fd97e01516dedcfb03e2a23Brian                fDatePattern==that->fDatePattern   &&
14722a47c5251ee7b91dc8f7f4f7dbeb3ad5a117b70Brian Paul                fTimePattern==that->fTimePattern   &&
14822a47c5251ee7b91dc8f7f4f7dbeb3ad5a117b70Brian Paul                fLocale==that->fLocale );
14922a47c5251ee7b91dc8f7f4f7dbeb3ad5a117b70Brian Paul    }
15022a47c5251ee7b91dc8f7f4f7dbeb3ad5a117b70Brian Paul    return FALSE;
15122a47c5251ee7b91dc8f7f4f7dbeb3ad5a117b70Brian Paul}
152af2aa8e9cf88a9ee3ec338eddc9a47bf2f142cb7Brian
153dc24230de7f913969b52dee3579bb8fa3d50a8c0Karl Schultzstatic const UChar APOSTROPHE = (UChar)0x0027;
15422a47c5251ee7b91dc8f7f4f7dbeb3ad5a117b70Brian Paul
15522a47c5251ee7b91dc8f7f4f7dbeb3ad5a117b70Brian PaulUnicodeString& RelativeDateFormat::format(  Calendar& cal,
156a803b0c891404dcd7c376e91f6a033cd4e42abc3Brian Paul                                UnicodeString& appendTo,
157e3a051e0538a605551f4d58294c94f5eb00ed07fKeith Whitwell                                FieldPosition& pos) const {
158e3a051e0538a605551f4d58294c94f5eb00ed07fKeith Whitwell
159b37a084357dd08573b86d6d8c5ba43d65bdc1bd7Brian Paul    UErrorCode status = U_ZERO_ERROR;
160e3a051e0538a605551f4d58294c94f5eb00ed07fKeith Whitwell    UnicodeString relativeDayString;
161e3a051e0538a605551f4d58294c94f5eb00ed07fKeith Whitwell    UDisplayContext capitalizationContext = getContext(UDISPCTX_TYPE_CAPITALIZATION, status);
16222a47c5251ee7b91dc8f7f4f7dbeb3ad5a117b70Brian Paul
163f9995b30756140724f41daf963fa06167912be7fKristian Høgsberg    // calculate the difference, in days, between 'cal' and now.
1649e8a961dd7d7b717a9fb4ecdea1c1b60ea355efeBrian    int dayDiff = dayDifference(cal, status);
16546b0988c673b28e072fd0cbf477632a9ab6f9f18Keith Whitwell
16646b0988c673b28e072fd0cbf477632a9ab6f9f18Keith Whitwell    // look up string
16746b0988c673b28e072fd0cbf477632a9ab6f9f18Keith Whitwell    int32_t len = 0;
1689e8a961dd7d7b717a9fb4ecdea1c1b60ea355efeBrian    const UChar *theString = getStringForDay(dayDiff, len, status);
1699e8a961dd7d7b717a9fb4ecdea1c1b60ea355efeBrian    if(U_SUCCESS(status) && (theString!=NULL)) {
1709e8a961dd7d7b717a9fb4ecdea1c1b60ea355efeBrian        // found a relative string
1719e8a961dd7d7b717a9fb4ecdea1c1b60ea355efeBrian        relativeDayString.setTo(theString, len);
1729e8a961dd7d7b717a9fb4ecdea1c1b60ea355efeBrian    }
1739e8a961dd7d7b717a9fb4ecdea1c1b60ea355efeBrian
1749e8a961dd7d7b717a9fb4ecdea1c1b60ea355efeBrian    if ( relativeDayString.length() > 0 && !fDatePattern.isEmpty() &&
1759e8a961dd7d7b717a9fb4ecdea1c1b60ea355efeBrian         (fTimePattern.isEmpty() || fCombinedFormat == NULL || fCombinedHasDateAtStart)) {
1769e8a961dd7d7b717a9fb4ecdea1c1b60ea355efeBrian#if !UCONFIG_NO_BREAK_ITERATION
1779e8a961dd7d7b717a9fb4ecdea1c1b60ea355efeBrian        // capitalize relativeDayString according to context for relative, set formatter no context
1789e8a961dd7d7b717a9fb4ecdea1c1b60ea355efeBrian        if ( u_islower(relativeDayString.char32At(0)) && fCapitalizationBrkIter!= NULL &&
1799e8a961dd7d7b717a9fb4ecdea1c1b60ea355efeBrian             ( capitalizationContext==UDISPCTX_CAPITALIZATION_FOR_BEGINNING_OF_SENTENCE ||
1809e8a961dd7d7b717a9fb4ecdea1c1b60ea355efeBrian               (capitalizationContext==UDISPCTX_CAPITALIZATION_FOR_UI_LIST_OR_MENU && fCapitalizationOfRelativeUnitsForUIListMenu) ||
1819e8a961dd7d7b717a9fb4ecdea1c1b60ea355efeBrian               (capitalizationContext==UDISPCTX_CAPITALIZATION_FOR_STANDALONE && fCapitalizationOfRelativeUnitsForStandAlone) ) ) {
1829e8a961dd7d7b717a9fb4ecdea1c1b60ea355efeBrian            // titlecase first word of relativeDayString
1839e8a961dd7d7b717a9fb4ecdea1c1b60ea355efeBrian            relativeDayString.toTitle(fCapitalizationBrkIter, fLocale, U_TITLECASE_NO_LOWERCASE | U_TITLECASE_NO_BREAK_ADJUSTMENT);
1849e8a961dd7d7b717a9fb4ecdea1c1b60ea355efeBrian        }
1859e8a961dd7d7b717a9fb4ecdea1c1b60ea355efeBrian#endif
1869e8a961dd7d7b717a9fb4ecdea1c1b60ea355efeBrian        fDateTimeFormatter->setContext(UDISPCTX_CAPITALIZATION_NONE, status);
1879e8a961dd7d7b717a9fb4ecdea1c1b60ea355efeBrian    } else {
1889e8a961dd7d7b717a9fb4ecdea1c1b60ea355efeBrian        // set our context for the formatter
18946b0988c673b28e072fd0cbf477632a9ab6f9f18Keith Whitwell        fDateTimeFormatter->setContext(capitalizationContext, status);
1909e8a961dd7d7b717a9fb4ecdea1c1b60ea355efeBrian    }
1919e8a961dd7d7b717a9fb4ecdea1c1b60ea355efeBrian
1929e8a961dd7d7b717a9fb4ecdea1c1b60ea355efeBrian    if (fDatePattern.isEmpty()) {
19346b0988c673b28e072fd0cbf477632a9ab6f9f18Keith Whitwell        fDateTimeFormatter->applyPattern(fTimePattern);
19446b0988c673b28e072fd0cbf477632a9ab6f9f18Keith Whitwell        fDateTimeFormatter->format(cal,appendTo,pos);
195e3a051e0538a605551f4d58294c94f5eb00ed07fKeith Whitwell    } else if (fTimePattern.isEmpty() || fCombinedFormat == NULL) {
196e3a051e0538a605551f4d58294c94f5eb00ed07fKeith Whitwell        if (relativeDayString.length() > 0) {
197233aafbb30594d0193b00705d0532be97060ebd1Brian Paul            appendTo.append(relativeDayString);
198233aafbb30594d0193b00705d0532be97060ebd1Brian Paul        } else {
199233aafbb30594d0193b00705d0532be97060ebd1Brian Paul            fDateTimeFormatter->applyPattern(fDatePattern);
200233aafbb30594d0193b00705d0532be97060ebd1Brian Paul            fDateTimeFormatter->format(cal,appendTo,pos);
201233aafbb30594d0193b00705d0532be97060ebd1Brian Paul        }
202233aafbb30594d0193b00705d0532be97060ebd1Brian Paul    } else {
203233aafbb30594d0193b00705d0532be97060ebd1Brian Paul        UnicodeString datePattern;
204233aafbb30594d0193b00705d0532be97060ebd1Brian Paul        if (relativeDayString.length() > 0) {
205298be2b028263b2c343a707662c6fbfa18293cb2Kristian Høgsberg            // Need to quote the relativeDayString to make it a legal date pattern
206233aafbb30594d0193b00705d0532be97060ebd1Brian Paul            relativeDayString.findAndReplace(UNICODE_STRING("'", 1), UNICODE_STRING("''", 2)); // double any existing APOSTROPHE
207233aafbb30594d0193b00705d0532be97060ebd1Brian Paul            relativeDayString.insert(0, APOSTROPHE); // add APOSTROPHE at beginning...
208233aafbb30594d0193b00705d0532be97060ebd1Brian Paul            relativeDayString.append(APOSTROPHE); // and at end
209233aafbb30594d0193b00705d0532be97060ebd1Brian Paul            datePattern.setTo(relativeDayString);
210233aafbb30594d0193b00705d0532be97060ebd1Brian Paul        } else {
211c123a9b2edc5852cb50485f344219508254081a8Brian Paul            datePattern.setTo(fDatePattern);
212233aafbb30594d0193b00705d0532be97060ebd1Brian Paul        }
213233aafbb30594d0193b00705d0532be97060ebd1Brian Paul        UnicodeString combinedPattern;
214233aafbb30594d0193b00705d0532be97060ebd1Brian Paul        Formattable timeDatePatterns[] = { fTimePattern, datePattern };
215233aafbb30594d0193b00705d0532be97060ebd1Brian Paul        fCombinedFormat->format(timeDatePatterns, 2, combinedPattern, pos, status); // pos is ignored by this
216233aafbb30594d0193b00705d0532be97060ebd1Brian Paul        fDateTimeFormatter->applyPattern(combinedPattern);
2179e8a961dd7d7b717a9fb4ecdea1c1b60ea355efeBrian        fDateTimeFormatter->format(cal,appendTo,pos);
218e3a051e0538a605551f4d58294c94f5eb00ed07fKeith Whitwell    }
219e3a051e0538a605551f4d58294c94f5eb00ed07fKeith Whitwell
220e3a051e0538a605551f4d58294c94f5eb00ed07fKeith Whitwell    return appendTo;
221e3a051e0538a605551f4d58294c94f5eb00ed07fKeith Whitwell}
222e3a051e0538a605551f4d58294c94f5eb00ed07fKeith Whitwell
223e3a051e0538a605551f4d58294c94f5eb00ed07fKeith Whitwell
224e3a051e0538a605551f4d58294c94f5eb00ed07fKeith Whitwell
225f9995b30756140724f41daf963fa06167912be7fKristian HøgsbergUnicodeString&
226e3a051e0538a605551f4d58294c94f5eb00ed07fKeith WhitwellRelativeDateFormat::format(const Formattable& obj,
227cd03ed4f54444d96e4e47cdb118a3dfd94d92bb0Keith Whitwell                         UnicodeString& appendTo,
2289e8a961dd7d7b717a9fb4ecdea1c1b60ea355efeBrian                         FieldPosition& pos,
2299e8a961dd7d7b717a9fb4ecdea1c1b60ea355efeBrian                         UErrorCode& status) const
2309e8a961dd7d7b717a9fb4ecdea1c1b60ea355efeBrian{
231e3a051e0538a605551f4d58294c94f5eb00ed07fKeith Whitwell    // this is just here to get around the hiding problem
232b37a084357dd08573b86d6d8c5ba43d65bdc1bd7Brian Paul    // (the previous format() override would hide the version of
233e3a051e0538a605551f4d58294c94f5eb00ed07fKeith Whitwell    // format() on DateFormat that this function correspond to, so we
234e3a051e0538a605551f4d58294c94f5eb00ed07fKeith Whitwell    // have to redefine it here)
2350070d398d13759adc519f9bc764ffd39bc88890eBrian Paul    return DateFormat::format(obj, appendTo, pos, status);
23664b4298181373d64ef2226935f70e9062536de8bBrian Paul}
237e3a051e0538a605551f4d58294c94f5eb00ed07fKeith Whitwell
238ccea3ff8a9eae16d2ca11b9cedef1318cffe3fb4Brian
2391c0f1dd42a50464eeb81de4aad8eecf24b3d6c89Chad Versacevoid RelativeDateFormat::parse( const UnicodeString& text,
2409e8a961dd7d7b717a9fb4ecdea1c1b60ea355efeBrian                    Calendar& cal,
2419e8a961dd7d7b717a9fb4ecdea1c1b60ea355efeBrian                    ParsePosition& pos) const {
2429e8a961dd7d7b717a9fb4ecdea1c1b60ea355efeBrian
243e3a051e0538a605551f4d58294c94f5eb00ed07fKeith Whitwell    int32_t startIndex = pos.getIndex();
2449e8a961dd7d7b717a9fb4ecdea1c1b60ea355efeBrian    if (fDatePattern.isEmpty()) {
245af2aa8e9cf88a9ee3ec338eddc9a47bf2f142cb7Brian        // no date pattern, try parsing as time
24622a47c5251ee7b91dc8f7f4f7dbeb3ad5a117b70Brian Paul        fDateTimeFormatter->applyPattern(fTimePattern);
24722a47c5251ee7b91dc8f7f4f7dbeb3ad5a117b70Brian Paul        fDateTimeFormatter->parse(text,cal,pos);
248efda5cb6263631175aa2efe46df9322b3c5775eeBrian    } else if (fTimePattern.isEmpty() || fCombinedFormat == NULL) {
249e6df80184b0c7e83eba382b3161ba11db497c55dIan Romanick        // no time pattern or way to combine, try parsing as date
250efda5cb6263631175aa2efe46df9322b3c5775eeBrian        // first check whether text matches a relativeDayString
251e6df80184b0c7e83eba382b3161ba11db497c55dIan Romanick        UBool matchedRelative = FALSE;
252efda5cb6263631175aa2efe46df9322b3c5775eeBrian        for (int n=0; n < fDatesLen && !matchedRelative; n++) {
25322a47c5251ee7b91dc8f7f4f7dbeb3ad5a117b70Brian Paul            if (fDates[n].string != NULL &&
254e3a051e0538a605551f4d58294c94f5eb00ed07fKeith Whitwell                    text.compare(startIndex, fDates[n].len, fDates[n].string) == 0) {
2559e8a961dd7d7b717a9fb4ecdea1c1b60ea355efeBrian                // it matched, handle the relative day string
256af2aa8e9cf88a9ee3ec338eddc9a47bf2f142cb7Brian                UErrorCode status = U_ZERO_ERROR;
2579e8a961dd7d7b717a9fb4ecdea1c1b60ea355efeBrian                matchedRelative = TRUE;
258e6df80184b0c7e83eba382b3161ba11db497c55dIan Romanick
259e3a051e0538a605551f4d58294c94f5eb00ed07fKeith Whitwell                // Set the calendar to now+offset
260e3a051e0538a605551f4d58294c94f5eb00ed07fKeith Whitwell                cal.setTime(Calendar::getNow(),status);
261b37a084357dd08573b86d6d8c5ba43d65bdc1bd7Brian Paul                cal.add(UCAL_DATE,fDates[n].offset, status);
26245bc887da226403f2c41077e40ca38b6f60f1359Brian Paul
263e3a051e0538a605551f4d58294c94f5eb00ed07fKeith Whitwell                if(U_FAILURE(status)) {
264e3a051e0538a605551f4d58294c94f5eb00ed07fKeith Whitwell                    // failure in setting calendar field, set offset to beginning of rel day string
265b37a084357dd08573b86d6d8c5ba43d65bdc1bd7Brian Paul                    pos.setErrorIndex(startIndex);
26645bc887da226403f2c41077e40ca38b6f60f1359Brian Paul                } else {
267e3a051e0538a605551f4d58294c94f5eb00ed07fKeith Whitwell                    pos.setIndex(startIndex + fDates[n].len);
268e3a051e0538a605551f4d58294c94f5eb00ed07fKeith Whitwell                }
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