]> git.saurik.com Git - apple/icu.git/blame - icuSources/i18n/reldtfmt.cpp
ICU-461.12.tar.gz
[apple/icu.git] / icuSources / i18n / reldtfmt.cpp
CommitLineData
46f4442e
A
1/*
2*******************************************************************************
729e4ab9
A
3* Copyright (C) 2007-2010, International Business Machines Corporation and
4* others. All Rights Reserved.
46f4442e
A
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
24U_NAMESPACE_BEGIN
25
26
27/**
28 * An array of URelativeString structs is used to store the resource data loaded out of the bundle.
29 */
30struct 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
36static const char DT_DateTimePatternsTag[]="DateTimePatterns";
37
38
39UOBJECT_DEFINE_RTTI_IMPLEMENTATION(RelativeDateFormat)
40
41RelativeDateFormat::RelativeDateFormat(const RelativeDateFormat& other) :
42DateFormat(other), fDateFormat(NULL), fTimeFormat(NULL), fCombinedFormat(NULL),
43fDateStyle(other.fDateStyle), fTimeStyle(other.fTimeStyle), fLocale(other.fLocale),
44fDayMin(other.fDayMin), fDayMax(other.fDayMax),
45fDatesLen(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
66RelativeDateFormat::RelativeDateFormat( UDateFormatStyle timeStyle, UDateFormatStyle dateStyle, const Locale& locale, UErrorCode& status)
67 : DateFormat(), fDateFormat(NULL), fTimeFormat(NULL), fCombinedFormat(NULL),
68fDateStyle(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
92RelativeDateFormat::~RelativeDateFormat() {
93 delete fDateFormat;
94 delete fTimeFormat;
95 delete fCombinedFormat;
96 uprv_free(fDates);
97}
98
99
100Format* RelativeDateFormat::clone(void) const {
101 return new RelativeDateFormat(*this);
102}
103
104UBool 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
115UnicodeString& 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
729e4ab9 127 int32_t len = 0;
46f4442e
A
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
166UnicodeString&
167RelativeDateFormat::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
180void 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
220UDate
221RelativeDateFormat::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
228UDate
229RelativeDateFormat::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
237const 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
258UnicodeString&
259RelativeDateFormat::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
285UnicodeString&
286RelativeDateFormat::toPatternDate(UnicodeString& result, UErrorCode& status) const
287{
288 if (!U_FAILURE(status)) {
289 result.remove();
290 if ( fDateFormat ) {
729e4ab9
A
291 SimpleDateFormat* sdtfmt = dynamic_cast<SimpleDateFormat*>(fDateFormat);
292 if (sdtfmt != NULL) {
293 sdtfmt->toPattern(result);
46f4442e
A
294 } else {
295 status = U_UNSUPPORTED_ERROR;
296 }
297 }
298 }
299 return result;
300}
301
302UnicodeString&
303RelativeDateFormat::toPatternTime(UnicodeString& result, UErrorCode& status) const
304{
305 if (!U_FAILURE(status)) {
306 result.remove();
307 if ( fTimeFormat ) {
729e4ab9
A
308 SimpleDateFormat* sdtfmt = dynamic_cast<SimpleDateFormat*>(fTimeFormat);
309 if (sdtfmt != NULL) {
310 sdtfmt->toPattern(result);
46f4442e
A
311 } else {
312 status = U_UNSUPPORTED_ERROR;
313 }
314 }
315 }
316 return result;
317}
318
319void
320RelativeDateFormat::applyPatterns(const UnicodeString& datePattern, const UnicodeString& timePattern, UErrorCode &status)
321{
322 if (!U_FAILURE(status)) {
729e4ab9
A
323 SimpleDateFormat* sdtfmt = NULL;
324 SimpleDateFormat* stmfmt = NULL;
325 if (fDateFormat && (sdtfmt = dynamic_cast<SimpleDateFormat*>(fDateFormat)) == NULL) {
46f4442e
A
326 status = U_UNSUPPORTED_ERROR;
327 return;
328 }
729e4ab9 329 if (fTimeFormat && (stmfmt = dynamic_cast<SimpleDateFormat*>(fTimeFormat)) == NULL) {
46f4442e
A
330 status = U_UNSUPPORTED_ERROR;
331 return;
332 }
333 if ( fDateFormat ) {
729e4ab9 334 sdtfmt->applyPattern(datePattern);
46f4442e
A
335 }
336 if ( fTimeFormat ) {
729e4ab9 337 stmfmt->applyPattern(timePattern);
46f4442e
A
338 }
339 }
340}
341
729e4ab9
A
342const DateFormatSymbols*
343RelativeDateFormat::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
46f4442e
A
352void RelativeDateFormat::loadDates(UErrorCode &status) {
353 CalendarData calData(fLocale, "gregorian", status);
354
355 UErrorCode tempStatus = status;
356 UResourceBundle *dateTimePatterns = calData.getByKey(DT_DateTimePatternsTag, tempStatus);
729e4ab9
A
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 }
46f4442e
A
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
450Calendar*
451RelativeDateFormat::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
462int32_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
479U_NAMESPACE_END
480
481#endif
482