• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2 *******************************************************************************
3 * Copyright (C) 2007-2011, 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 //#define DEBUG_RELDTFMT
13 
14 #include <stdio.h>
15 #include <stdlib.h>
16 
17 #include "reldtfmt.h"
18 #include "unicode/msgfmt.h"
19 #include "unicode/smpdtfmt.h"
20 
21 #include "gregoimp.h" // for CalendarData
22 #include "cmemory.h"
23 
24 U_NAMESPACE_BEGIN
25 
26 
27 /**
28  * An array of URelativeString structs is used to store the resource data loaded out of the bundle.
29  */
30 struct URelativeString {
31     int32_t offset;         /** offset of this item, such as, the relative date **/
32     int32_t len;            /** length of the string **/
33     const UChar* string;    /** string, or NULL if not set **/
34 };
35 
36 static const char DT_DateTimePatternsTag[]="DateTimePatterns";
37 
38 
UOBJECT_DEFINE_RTTI_IMPLEMENTATION(RelativeDateFormat)39 UOBJECT_DEFINE_RTTI_IMPLEMENTATION(RelativeDateFormat)
40 
41 RelativeDateFormat::RelativeDateFormat(const RelativeDateFormat& other) :
42 DateFormat(other), fDateFormat(NULL), fTimeFormat(NULL), fCombinedFormat(NULL),
43 fDateStyle(other.fDateStyle), fTimeStyle(other.fTimeStyle), fLocale(other.fLocale),
44 fDayMin(other.fDayMin), fDayMax(other.fDayMax),
45 fDatesLen(other.fDatesLen), fDates(NULL)
46 {
47     if(other.fDateFormat != NULL) {
48         fDateFormat = (DateFormat*)other.fDateFormat->clone();
49     } else {
50         fDateFormat = NULL;
51     }
52     if (fDatesLen > 0) {
53         fDates = (URelativeString*) uprv_malloc(sizeof(fDates[0])*fDatesLen);
54         uprv_memcpy(fDates, other.fDates, sizeof(fDates[0])*fDatesLen);
55     }
56     //fCalendar = other.fCalendar->clone();
57 /*
58     if(other.fTimeFormat != NULL) {
59         fTimeFormat = (DateFormat*)other.fTimeFormat->clone();
60     } else {
61         fTimeFormat = NULL;
62     }
63 */
64 }
65 
RelativeDateFormat(UDateFormatStyle timeStyle,UDateFormatStyle dateStyle,const Locale & locale,UErrorCode & status)66 RelativeDateFormat::RelativeDateFormat( UDateFormatStyle timeStyle, UDateFormatStyle dateStyle, const Locale& locale, UErrorCode& status)
67  : DateFormat(), fDateFormat(NULL), fTimeFormat(NULL), fCombinedFormat(NULL),
68 fDateStyle(dateStyle), fTimeStyle(timeStyle), fLocale(locale), fDatesLen(0), fDates(NULL)
69  {
70     if(U_FAILURE(status) ) {
71         return;
72     }
73 
74     if(fDateStyle != UDAT_NONE) {
75         EStyle newStyle = (EStyle)(fDateStyle & ~UDAT_RELATIVE);
76         // Create a DateFormat in the non-relative style requested.
77         fDateFormat = createDateInstance(newStyle, locale);
78     }
79     if(fTimeStyle >= UDAT_FULL && fTimeStyle <= UDAT_SHORT) {
80         fTimeFormat = createTimeInstance((EStyle)fTimeStyle, locale);
81     } else if(fTimeStyle != UDAT_NONE) {
82         // don't support other time styles (e.g. relative styles), for now
83         status = U_ILLEGAL_ARGUMENT_ERROR;
84         return;
85     }
86 
87     // Initialize the parent fCalendar, so that parse() works correctly.
88     initializeCalendar(NULL, locale, status);
89     loadDates(status);
90 }
91 
~RelativeDateFormat()92 RelativeDateFormat::~RelativeDateFormat() {
93     delete fDateFormat;
94     delete fTimeFormat;
95     delete fCombinedFormat;
96     uprv_free(fDates);
97 }
98 
99 
clone(void) const100 Format* RelativeDateFormat::clone(void) const {
101     return new RelativeDateFormat(*this);
102 }
103 
operator ==(const Format & other) const104 UBool RelativeDateFormat::operator==(const Format& other) const {
105     if(DateFormat::operator==(other)) {
106         // DateFormat::operator== guarantees following cast is safe
107         RelativeDateFormat* that = (RelativeDateFormat*)&other;
108         return (fDateStyle==that->fDateStyle   &&
109                 fTimeStyle==that->fTimeStyle   &&
110                 fLocale==that->fLocale);
111     }
112     return FALSE;
113 }
114 
format(Calendar & cal,UnicodeString & appendTo,FieldPosition & pos) const115 UnicodeString& RelativeDateFormat::format(  Calendar& cal,
116                                 UnicodeString& appendTo,
117                                 FieldPosition& pos) const {
118 
119     UErrorCode status = U_ZERO_ERROR;
120     UChar emptyStr = 0;
121     UnicodeString dateString(&emptyStr);
122 
123     // calculate the difference, in days, between 'cal' and now.
124     int dayDiff = dayDifference(cal, status);
125 
126     // look up string
127     int32_t len = 0;
128     const UChar *theString = getStringForDay(dayDiff, len, status);
129     if(U_SUCCESS(status) && (theString!=NULL)) {
130         // found a relative string
131         dateString.setTo(theString, len);
132     }
133 
134     if(fTimeFormat == NULL || fCombinedFormat == 0) {
135         if (dateString.length() > 0) {
136             appendTo.append(dateString);
137         } else if(fDateFormat != NULL) {
138             fDateFormat->format(cal,appendTo,pos);
139         }
140     } else {
141         if (dateString.length() == 0 && fDateFormat != NULL) {
142             fDateFormat->format(cal,dateString,pos);
143         }
144         UnicodeString timeString(&emptyStr);
145         FieldPosition timepos = pos;
146         fTimeFormat->format(cal,timeString,timepos);
147         Formattable timeDateStrings[] = { timeString, dateString };
148         fCombinedFormat->format(timeDateStrings, 2, appendTo, pos, status); // pos is ignored by this
149         int32_t offset;
150         if (pos.getEndIndex() > 0 && (offset = appendTo.indexOf(dateString)) >= 0) {
151             // pos.field was found in dateString, offset start & end based on final position of dateString
152             pos.setBeginIndex( pos.getBeginIndex() + offset );
153             pos.setEndIndex( pos.getEndIndex() + offset );
154         } else if (timepos.getEndIndex() > 0 && (offset = appendTo.indexOf(timeString)) >= 0) {
155             // pos.field was found in timeString, offset start & end based on final position of timeString
156             pos.setBeginIndex( timepos.getBeginIndex() + offset );
157             pos.setEndIndex( timepos.getEndIndex() + offset );
158         }
159     }
160 
161     return appendTo;
162 }
163 
164 
165 
166 UnicodeString&
format(const Formattable & obj,UnicodeString & appendTo,FieldPosition & pos,UErrorCode & status) const167 RelativeDateFormat::format(const Formattable& obj,
168                          UnicodeString& appendTo,
169                          FieldPosition& pos,
170                          UErrorCode& status) const
171 {
172     // this is just here to get around the hiding problem
173     // (the previous format() override would hide the version of
174     // format() on DateFormat that this function correspond to, so we
175     // have to redefine it here)
176     return DateFormat::format(obj, appendTo, pos, status);
177 }
178 
179 
parse(const UnicodeString & text,Calendar & cal,ParsePosition & pos) const180 void RelativeDateFormat::parse( const UnicodeString& text,
181                     Calendar& cal,
182                     ParsePosition& pos) const {
183 
184     // Can the fDateFormat parse it?
185     if(fDateFormat != NULL) {
186         ParsePosition aPos(pos);
187         fDateFormat->parse(text,cal,aPos);
188         if((aPos.getIndex() != pos.getIndex()) &&
189             (aPos.getErrorIndex()==-1)) {
190                 pos=aPos; // copy the sub parse
191                 return; // parsed subfmt OK
192         }
193     }
194 
195     // Linear search the relative strings
196     for(int n=0;n<fDatesLen;n++) {
197         if(fDates[n].string != NULL &&
198             (0==text.compare(pos.getIndex(),
199                          fDates[n].len,
200                          fDates[n].string))) {
201             UErrorCode status = U_ZERO_ERROR;
202 
203             // Set the calendar to now+offset
204             cal.setTime(Calendar::getNow(),status);
205             cal.add(UCAL_DATE,fDates[n].offset, status);
206 
207             if(U_FAILURE(status)) {
208                 // failure in setting calendar fields
209                 pos.setErrorIndex(pos.getIndex()+fDates[n].len);
210             } else {
211                 pos.setIndex(pos.getIndex()+fDates[n].len);
212             }
213             return;
214         }
215     }
216 
217     // parse failed
218 }
219 
220 UDate
parse(const UnicodeString & text,ParsePosition & pos) const221 RelativeDateFormat::parse( const UnicodeString& text,
222                          ParsePosition& pos) const {
223     // redefined here because the other parse() function hides this function's
224     // cunterpart on DateFormat
225     return DateFormat::parse(text, pos);
226 }
227 
228 UDate
parse(const UnicodeString & text,UErrorCode & status) const229 RelativeDateFormat::parse(const UnicodeString& text, UErrorCode& status) const
230 {
231     // redefined here because the other parse() function hides this function's
232     // counterpart on DateFormat
233     return DateFormat::parse(text, status);
234 }
235 
236 
getStringForDay(int32_t day,int32_t & len,UErrorCode & status) const237 const UChar *RelativeDateFormat::getStringForDay(int32_t day, int32_t &len, UErrorCode &status) const {
238     if(U_FAILURE(status)) {
239         return NULL;
240     }
241 
242     // Is it outside the resource bundle's range?
243     if(day < fDayMin || day > fDayMax) {
244         return NULL; // don't have it.
245     }
246 
247     // Linear search the held strings
248     for(int n=0;n<fDatesLen;n++) {
249         if(fDates[n].offset == day) {
250             len = fDates[n].len;
251             return fDates[n].string;
252         }
253     }
254 
255     return NULL;  // not found.
256 }
257 
258 UnicodeString&
toPattern(UnicodeString & result,UErrorCode & status) const259 RelativeDateFormat::toPattern(UnicodeString& result, UErrorCode& status) const
260 {
261     if (!U_FAILURE(status)) {
262         result.remove();
263         if (fTimeFormat == NULL || fCombinedFormat == 0) {
264             if (fDateFormat != NULL) {
265                 UnicodeString datePattern;
266                 this->toPatternDate(datePattern, status);
267                 if (!U_FAILURE(status)) {
268                     result.setTo(datePattern);
269                 }
270             }
271         } else {
272             UnicodeString datePattern, timePattern;
273             this->toPatternDate(datePattern, status);
274             this->toPatternTime(timePattern, status);
275             if (!U_FAILURE(status)) {
276                 Formattable timeDatePatterns[] = { timePattern, datePattern };
277                 FieldPosition pos;
278                 fCombinedFormat->format(timeDatePatterns, 2, result, pos, status);
279             }
280         }
281     }
282     return result;
283 }
284 
285 UnicodeString&
toPatternDate(UnicodeString & result,UErrorCode & status) const286 RelativeDateFormat::toPatternDate(UnicodeString& result, UErrorCode& status) const
287 {
288     if (!U_FAILURE(status)) {
289         result.remove();
290         if ( fDateFormat ) {
291             SimpleDateFormat* sdtfmt = dynamic_cast<SimpleDateFormat*>(fDateFormat);
292             if (sdtfmt != NULL) {
293                 sdtfmt->toPattern(result);
294             } else {
295                 status = U_UNSUPPORTED_ERROR;
296             }
297         }
298     }
299     return result;
300 }
301 
302 UnicodeString&
toPatternTime(UnicodeString & result,UErrorCode & status) const303 RelativeDateFormat::toPatternTime(UnicodeString& result, UErrorCode& status) const
304 {
305     if (!U_FAILURE(status)) {
306         result.remove();
307         if ( fTimeFormat ) {
308             SimpleDateFormat* sdtfmt = dynamic_cast<SimpleDateFormat*>(fTimeFormat);
309             if (sdtfmt != NULL) {
310                 sdtfmt->toPattern(result);
311             } else {
312                 status = U_UNSUPPORTED_ERROR;
313             }
314         }
315     }
316     return result;
317 }
318 
319 void
applyPatterns(const UnicodeString & datePattern,const UnicodeString & timePattern,UErrorCode & status)320 RelativeDateFormat::applyPatterns(const UnicodeString& datePattern, const UnicodeString& timePattern, UErrorCode &status)
321 {
322     if (!U_FAILURE(status)) {
323         SimpleDateFormat* sdtfmt = NULL;
324         SimpleDateFormat* stmfmt = NULL;
325         if (fDateFormat && (sdtfmt = dynamic_cast<SimpleDateFormat*>(fDateFormat)) == NULL) {
326             status = U_UNSUPPORTED_ERROR;
327             return;
328         }
329         if (fTimeFormat && (stmfmt = dynamic_cast<SimpleDateFormat*>(fTimeFormat)) == NULL) {
330             status = U_UNSUPPORTED_ERROR;
331             return;
332         }
333         if ( fDateFormat ) {
334             sdtfmt->applyPattern(datePattern);
335         }
336         if ( fTimeFormat ) {
337             stmfmt->applyPattern(timePattern);
338         }
339     }
340 }
341 
342 const DateFormatSymbols*
getDateFormatSymbols() const343 RelativeDateFormat::getDateFormatSymbols() const
344 {
345     SimpleDateFormat* sdtfmt = NULL;
346     if (fDateFormat && (sdtfmt = dynamic_cast<SimpleDateFormat*>(fDateFormat)) != NULL) {
347         return sdtfmt->getDateFormatSymbols();
348     }
349     return NULL;
350 }
351 
loadDates(UErrorCode & status)352 void RelativeDateFormat::loadDates(UErrorCode &status) {
353     CalendarData calData(fLocale, "gregorian", status);
354 
355     UErrorCode tempStatus = status;
356     UResourceBundle *dateTimePatterns = calData.getByKey(DT_DateTimePatternsTag, tempStatus);
357     if(U_SUCCESS(tempStatus)) {
358         int32_t patternsSize = ures_getSize(dateTimePatterns);
359         if (patternsSize > kDateTime) {
360             int32_t resStrLen = 0;
361 
362             int32_t glueIndex = kDateTime;
363             if (patternsSize >= (DateFormat::kDateTimeOffset + DateFormat::kShort + 1)) {
364                 // Get proper date time format
365                 switch (fDateStyle) {
366                 case kFullRelative:
367                 case kFull:
368                     glueIndex = kDateTimeOffset + kFull;
369                     break;
370                 case kLongRelative:
371                 case kLong:
372                     glueIndex = kDateTimeOffset + kLong;
373                     break;
374                 case kMediumRelative:
375                 case kMedium:
376                     glueIndex = kDateTimeOffset + kMedium;
377                     break;
378                 case kShortRelative:
379                 case kShort:
380                     glueIndex = kDateTimeOffset + kShort;
381                     break;
382                 default:
383                     break;
384                 }
385             }
386 
387             const UChar *resStr = ures_getStringByIndex(dateTimePatterns, glueIndex, &resStrLen, &tempStatus);
388             fCombinedFormat = new MessageFormat(UnicodeString(TRUE, resStr, resStrLen), fLocale, tempStatus);
389         }
390     }
391 
392     UResourceBundle *strings = calData.getByKey3("fields", "day", "relative", status);
393     // set up min/max
394     fDayMin=-1;
395     fDayMax=1;
396 
397     if(U_FAILURE(status)) {
398         fDatesLen=0;
399         return;
400     }
401 
402     fDatesLen = ures_getSize(strings);
403     fDates = (URelativeString*) uprv_malloc(sizeof(fDates[0])*fDatesLen);
404 
405     // Load in each item into the array...
406     int n = 0;
407 
408     UResourceBundle *subString = NULL;
409 
410     while(ures_hasNext(strings) && U_SUCCESS(status)) {  // iterate over items
411         subString = ures_getNextResource(strings, subString, &status);
412 
413         if(U_FAILURE(status) || (subString==NULL)) break;
414 
415         // key = offset #
416         const char *key = ures_getKey(subString);
417 
418         // load the string and length
419         int32_t aLen;
420         const UChar* aString = ures_getString(subString, &aLen, &status);
421 
422         if(U_FAILURE(status) || aString == NULL) break;
423 
424         // calculate the offset
425         int32_t offset = atoi(key);
426 
427         // set min/max
428         if(offset < fDayMin) {
429             fDayMin = offset;
430         }
431         if(offset > fDayMax) {
432             fDayMax = offset;
433         }
434 
435         // copy the string pointer
436         fDates[n].offset = offset;
437         fDates[n].string = aString;
438         fDates[n].len = aLen;
439 
440         n++;
441     }
442     ures_close(subString);
443 
444     // the fDates[] array could be sorted here, for direct access.
445 }
446 
447 
448 // this should to be in DateFormat, instead it was copied from SimpleDateFormat.
449 
450 Calendar*
initializeCalendar(TimeZone * adoptZone,const Locale & locale,UErrorCode & status)451 RelativeDateFormat::initializeCalendar(TimeZone* adoptZone, const Locale& locale, UErrorCode& status)
452 {
453     if(!U_FAILURE(status)) {
454         fCalendar = Calendar::createInstance(adoptZone?adoptZone:TimeZone::createDefault(), locale, status);
455     }
456     if (U_SUCCESS(status) && fCalendar == NULL) {
457         status = U_MEMORY_ALLOCATION_ERROR;
458     }
459     return fCalendar;
460 }
461 
dayDifference(Calendar & cal,UErrorCode & status)462 int32_t RelativeDateFormat::dayDifference(Calendar &cal, UErrorCode &status) {
463     if(U_FAILURE(status)) {
464         return 0;
465     }
466     // TODO: Cache the nowCal to avoid heap allocs? Would be difficult, don't know the calendar type
467     Calendar *nowCal = cal.clone();
468     nowCal->setTime(Calendar::getNow(), status);
469 
470     // For the day difference, we are interested in the difference in the (modified) julian day number
471     // which is midnight to midnight.  Using fieldDifference() is NOT correct here, because
472     // 6pm Jan 4th  to 10am Jan 5th should be considered "tomorrow".
473     int32_t dayDiff = cal.get(UCAL_JULIAN_DAY, status) - nowCal->get(UCAL_JULIAN_DAY, status);
474 
475     delete nowCal;
476     return dayDiff;
477 }
478 
479 U_NAMESPACE_END
480 
481 #endif
482 
483