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
26 U_NAMESPACE_BEGIN
27
28
29 /**
30 * An array of URelativeString structs is used to store the resource data loaded out of the bundle.
31 */
32 struct 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
38 static const char DT_DateTimePatternsTag[]="DateTimePatterns";
39
40
UOBJECT_DEFINE_RTTI_IMPLEMENTATION(RelativeDateFormat)41 UOBJECT_DEFINE_RTTI_IMPLEMENTATION(RelativeDateFormat)
42
43 RelativeDateFormat::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
RelativeDateFormat(UDateFormatStyle timeStyle,UDateFormatStyle dateStyle,const Locale & locale,UErrorCode & status)72 RelativeDateFormat::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
~RelativeDateFormat()125 RelativeDateFormat::~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
clone(void) const135 Format* RelativeDateFormat::clone(void) const {
136 return new RelativeDateFormat(*this);
137 }
138
operator ==(const Format & other) const139 UBool 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
153 static const UChar APOSTROPHE = (UChar)0x0027;
154
format(Calendar & cal,UnicodeString & appendTo,FieldPosition & pos) const155 UnicodeString& 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
225 UnicodeString&
format(const Formattable & obj,UnicodeString & appendTo,FieldPosition & pos,UErrorCode & status) const226 RelativeDateFormat::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
parse(const UnicodeString & text,Calendar & cal,ParsePosition & pos) const239 void 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
336 UDate
parse(const UnicodeString & text,ParsePosition & pos) const337 RelativeDateFormat::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
344 UDate
parse(const UnicodeString & text,UErrorCode & status) const345 RelativeDateFormat::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
getStringForDay(int32_t day,int32_t & len,UErrorCode & status) const353 const 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
374 UnicodeString&
toPattern(UnicodeString & result,UErrorCode & status) const375 RelativeDateFormat::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
392 UnicodeString&
toPatternDate(UnicodeString & result,UErrorCode & status) const393 RelativeDateFormat::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
402 UnicodeString&
toPatternTime(UnicodeString & result,UErrorCode & status) const403 RelativeDateFormat::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
412 void
applyPatterns(const UnicodeString & datePattern,const UnicodeString & timePattern,UErrorCode & status)413 RelativeDateFormat::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
421 const DateFormatSymbols*
getDateFormatSymbols() const422 RelativeDateFormat::getDateFormatSymbols() const
423 {
424 return fDateTimeFormatter->getDateFormatSymbols();
425 }
426
427 // override the DateFormat implementation in order to
428 // lazily initialize relevant items
429 void
setContext(UDisplayContext value,UErrorCode & status)430 RelativeDateFormat::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
454 void
initCapitalizationContextInfo(const Locale & thelocale)455 RelativeDateFormat::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
475 static const UChar patItem1[] = {0x7B,0x31,0x7D}; // "{1}"
476 static const int32_t patItem1Len = 3;
477
loadDates(UErrorCode & status)478 void 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
585 Calendar*
initializeCalendar(TimeZone * adoptZone,const Locale & locale,UErrorCode & status)586 RelativeDateFormat::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
dayDifference(Calendar & cal,UErrorCode & status)597 int32_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
614 U_NAMESPACE_END
615
616 #endif
617
618