]>
Commit | Line | Data |
---|---|---|
46f4442e A |
1 | /* |
2 | ******************************************************************************* | |
51004dcb | 3 | * Copyright (C) 2007-2013, International Business Machines Corporation and |
729e4ab9 | 4 | * others. All Rights Reserved. |
46f4442e A |
5 | ******************************************************************************* |
6 | */ | |
7 | ||
8 | #include "unicode/utypes.h" | |
9 | ||
10 | #if !UCONFIG_NO_FORMATTING | |
11 | ||
46f4442e A |
12 | #include <stdlib.h> |
13 | ||
14 | #include "reldtfmt.h" | |
51004dcb | 15 | #include "unicode/datefmt.h" |
46f4442e | 16 | #include "unicode/smpdtfmt.h" |
51004dcb | 17 | #include "unicode/msgfmt.h" |
46f4442e A |
18 | |
19 | #include "gregoimp.h" // for CalendarData | |
20 | #include "cmemory.h" | |
51004dcb | 21 | #include "uresimp.h" |
46f4442e A |
22 | |
23 | U_NAMESPACE_BEGIN | |
24 | ||
25 | ||
26 | /** | |
27 | * An array of URelativeString structs is used to store the resource data loaded out of the bundle. | |
28 | */ | |
29 | struct URelativeString { | |
30 | int32_t offset; /** offset of this item, such as, the relative date **/ | |
31 | int32_t len; /** length of the string **/ | |
32 | const UChar* string; /** string, or NULL if not set **/ | |
33 | }; | |
34 | ||
35 | static const char DT_DateTimePatternsTag[]="DateTimePatterns"; | |
36 | ||
37 | ||
38 | UOBJECT_DEFINE_RTTI_IMPLEMENTATION(RelativeDateFormat) | |
39 | ||
40 | RelativeDateFormat::RelativeDateFormat(const RelativeDateFormat& other) : | |
51004dcb A |
41 | DateFormat(other), fDateTimeFormatter(NULL), fDatePattern(other.fDatePattern), |
42 | fTimePattern(other.fTimePattern), fCombinedFormat(NULL), | |
43 | fDateStyle(other.fDateStyle), fLocale(other.fLocale), | |
44 | fDayMin(other.fDayMin), fDayMax(other.fDayMax), | |
45 | fDatesLen(other.fDatesLen), fDates(NULL) | |
46f4442e | 46 | { |
51004dcb A |
47 | if(other.fDateTimeFormatter != NULL) { |
48 | fDateTimeFormatter = (SimpleDateFormat*)other.fDateTimeFormatter->clone(); | |
49 | } | |
50 | if(other.fCombinedFormat != NULL) { | |
51 | fCombinedFormat = (MessageFormat*)other.fCombinedFormat->clone(); | |
46f4442e A |
52 | } |
53 | if (fDatesLen > 0) { | |
54 | fDates = (URelativeString*) uprv_malloc(sizeof(fDates[0])*fDatesLen); | |
55 | uprv_memcpy(fDates, other.fDates, sizeof(fDates[0])*fDatesLen); | |
56 | } | |
46f4442e A |
57 | } |
58 | ||
51004dcb A |
59 | RelativeDateFormat::RelativeDateFormat( UDateFormatStyle timeStyle, UDateFormatStyle dateStyle, |
60 | const Locale& locale, UErrorCode& status) : | |
61 | DateFormat(), fDateTimeFormatter(NULL), fDatePattern(), fTimePattern(), fCombinedFormat(NULL), | |
62 | fDateStyle(dateStyle), fLocale(locale), fDatesLen(0), fDates(NULL) | |
63 | { | |
46f4442e A |
64 | if(U_FAILURE(status) ) { |
65 | return; | |
66 | } | |
67 | ||
51004dcb | 68 | if (timeStyle < UDAT_NONE || timeStyle > UDAT_SHORT) { |
46f4442e A |
69 | // don't support other time styles (e.g. relative styles), for now |
70 | status = U_ILLEGAL_ARGUMENT_ERROR; | |
71 | return; | |
72 | } | |
51004dcb A |
73 | UDateFormatStyle baseDateStyle = (dateStyle > UDAT_SHORT)? (UDateFormatStyle)(dateStyle & ~UDAT_RELATIVE): dateStyle; |
74 | DateFormat * df; | |
75 | // Get fDateTimeFormatter from either date or time style (does not matter, we will override the pattern). | |
76 | // We do need to get separate patterns for the date & time styles. | |
77 | if (baseDateStyle != UDAT_NONE) { | |
78 | df = createDateInstance((EStyle)baseDateStyle, locale); | |
79 | fDateTimeFormatter=dynamic_cast<SimpleDateFormat *>(df); | |
80 | if (fDateTimeFormatter == NULL) { | |
81 | status = U_UNSUPPORTED_ERROR; | |
82 | return; | |
83 | } | |
84 | fDateTimeFormatter->toPattern(fDatePattern); | |
85 | if (timeStyle != UDAT_NONE) { | |
86 | df = createTimeInstance((EStyle)timeStyle, locale); | |
87 | SimpleDateFormat *sdf = dynamic_cast<SimpleDateFormat *>(df); | |
88 | if (sdf != NULL) { | |
89 | sdf->toPattern(fTimePattern); | |
90 | delete sdf; | |
91 | } | |
92 | } | |
93 | } else { | |
94 | // does not matter whether timeStyle is UDAT_NONE, we need something for fDateTimeFormatter | |
95 | df = createTimeInstance((EStyle)timeStyle, locale); | |
96 | fDateTimeFormatter=dynamic_cast<SimpleDateFormat *>(df); | |
97 | if (fDateTimeFormatter == NULL) { | |
98 | status = U_UNSUPPORTED_ERROR; | |
99 | return; | |
100 | } | |
101 | fDateTimeFormatter->toPattern(fTimePattern); | |
102 | } | |
46f4442e A |
103 | |
104 | // Initialize the parent fCalendar, so that parse() works correctly. | |
105 | initializeCalendar(NULL, locale, status); | |
106 | loadDates(status); | |
107 | } | |
108 | ||
109 | RelativeDateFormat::~RelativeDateFormat() { | |
51004dcb | 110 | delete fDateTimeFormatter; |
46f4442e A |
111 | delete fCombinedFormat; |
112 | uprv_free(fDates); | |
113 | } | |
114 | ||
115 | ||
116 | Format* RelativeDateFormat::clone(void) const { | |
117 | return new RelativeDateFormat(*this); | |
118 | } | |
119 | ||
120 | UBool RelativeDateFormat::operator==(const Format& other) const { | |
121 | if(DateFormat::operator==(other)) { | |
122 | // DateFormat::operator== guarantees following cast is safe | |
123 | RelativeDateFormat* that = (RelativeDateFormat*)&other; | |
124 | return (fDateStyle==that->fDateStyle && | |
51004dcb A |
125 | fDatePattern==that->fDatePattern && |
126 | fTimePattern==that->fTimePattern && | |
46f4442e A |
127 | fLocale==that->fLocale); |
128 | } | |
129 | return FALSE; | |
130 | } | |
131 | ||
51004dcb A |
132 | static const UChar APOSTROPHE = (UChar)0x0027; |
133 | ||
46f4442e A |
134 | UnicodeString& RelativeDateFormat::format( Calendar& cal, |
135 | UnicodeString& appendTo, | |
136 | FieldPosition& pos) const { | |
137 | ||
138 | UErrorCode status = U_ZERO_ERROR; | |
51004dcb | 139 | UnicodeString relativeDayString; |
46f4442e A |
140 | |
141 | // calculate the difference, in days, between 'cal' and now. | |
142 | int dayDiff = dayDifference(cal, status); | |
143 | ||
144 | // look up string | |
729e4ab9 | 145 | int32_t len = 0; |
46f4442e A |
146 | const UChar *theString = getStringForDay(dayDiff, len, status); |
147 | if(U_SUCCESS(status) && (theString!=NULL)) { | |
148 | // found a relative string | |
51004dcb | 149 | relativeDayString.setTo(theString, len); |
46f4442e A |
150 | } |
151 | ||
51004dcb A |
152 | if (fDatePattern.isEmpty()) { |
153 | fDateTimeFormatter->applyPattern(fTimePattern); | |
154 | fDateTimeFormatter->format(cal,appendTo,pos); | |
155 | } else if (fTimePattern.isEmpty() || fCombinedFormat == NULL) { | |
156 | if (relativeDayString.length() > 0) { | |
157 | appendTo.append(relativeDayString); | |
158 | } else { | |
159 | fDateTimeFormatter->applyPattern(fDatePattern); | |
160 | fDateTimeFormatter->format(cal,appendTo,pos); | |
46f4442e A |
161 | } |
162 | } else { | |
51004dcb A |
163 | UnicodeString datePattern; |
164 | if (relativeDayString.length() > 0) { | |
165 | // Need to quote the relativeDayString to make it a legal date pattern | |
166 | relativeDayString.findAndReplace(UNICODE_STRING("'", 1), UNICODE_STRING("''", 2)); // double any existing APOSTROPHE | |
167 | relativeDayString.insert(0, APOSTROPHE); // add APOSTROPHE at beginning... | |
168 | relativeDayString.append(APOSTROPHE); // and at end | |
169 | datePattern.setTo(relativeDayString); | |
170 | } else { | |
171 | datePattern.setTo(fDatePattern); | |
46f4442e | 172 | } |
51004dcb A |
173 | UnicodeString combinedPattern; |
174 | Formattable timeDatePatterns[] = { fTimePattern, datePattern }; | |
175 | fCombinedFormat->format(timeDatePatterns, 2, combinedPattern, pos, status); // pos is ignored by this | |
176 | fDateTimeFormatter->applyPattern(combinedPattern); | |
177 | fDateTimeFormatter->format(cal,appendTo,pos); | |
46f4442e A |
178 | } |
179 | ||
180 | return appendTo; | |
181 | } | |
182 | ||
183 | ||
184 | ||
185 | UnicodeString& | |
186 | RelativeDateFormat::format(const Formattable& obj, | |
187 | UnicodeString& appendTo, | |
188 | FieldPosition& pos, | |
189 | UErrorCode& status) const | |
190 | { | |
191 | // this is just here to get around the hiding problem | |
192 | // (the previous format() override would hide the version of | |
193 | // format() on DateFormat that this function correspond to, so we | |
194 | // have to redefine it here) | |
195 | return DateFormat::format(obj, appendTo, pos, status); | |
196 | } | |
197 | ||
198 | ||
199 | void RelativeDateFormat::parse( const UnicodeString& text, | |
200 | Calendar& cal, | |
201 | ParsePosition& pos) const { | |
202 | ||
51004dcb A |
203 | int32_t startIndex = pos.getIndex(); |
204 | if (fDatePattern.isEmpty()) { | |
205 | // no date pattern, try parsing as time | |
206 | fDateTimeFormatter->applyPattern(fTimePattern); | |
207 | fDateTimeFormatter->parse(text,cal,pos); | |
208 | } else if (fTimePattern.isEmpty() || fCombinedFormat == NULL) { | |
209 | // no time pattern or way to combine, try parsing as date | |
210 | // first check whether text matches a relativeDayString | |
211 | UBool matchedRelative = FALSE; | |
212 | for (int n=0; n < fDatesLen && !matchedRelative; n++) { | |
213 | if (fDates[n].string != NULL && | |
214 | text.compare(startIndex, fDates[n].len, fDates[n].string) == 0) { | |
215 | // it matched, handle the relative day string | |
216 | UErrorCode status = U_ZERO_ERROR; | |
217 | matchedRelative = TRUE; | |
218 | ||
219 | // Set the calendar to now+offset | |
220 | cal.setTime(Calendar::getNow(),status); | |
221 | cal.add(UCAL_DATE,fDates[n].offset, status); | |
222 | ||
223 | if(U_FAILURE(status)) { | |
224 | // failure in setting calendar field, set offset to beginning of rel day string | |
225 | pos.setErrorIndex(startIndex); | |
226 | } else { | |
227 | pos.setIndex(startIndex + fDates[n].len); | |
228 | } | |
229 | } | |
46f4442e | 230 | } |
51004dcb A |
231 | if (!matchedRelative) { |
232 | // just parse as normal date | |
233 | fDateTimeFormatter->applyPattern(fDatePattern); | |
234 | fDateTimeFormatter->parse(text,cal,pos); | |
235 | } | |
236 | } else { | |
237 | // Here we replace any relativeDayString in text with the equivalent date | |
238 | // formatted per fDatePattern, then parse text normally using the combined pattern. | |
239 | UnicodeString modifiedText(text); | |
240 | FieldPosition fPos; | |
241 | int32_t dateStart = 0, origDateLen = 0, modDateLen = 0; | |
242 | UErrorCode status = U_ZERO_ERROR; | |
243 | for (int n=0; n < fDatesLen; n++) { | |
244 | int32_t relativeStringOffset; | |
245 | if (fDates[n].string != NULL && | |
246 | (relativeStringOffset = modifiedText.indexOf(fDates[n].string, fDates[n].len, startIndex)) >= startIndex) { | |
247 | // it matched, replace the relative date with a real one for parsing | |
248 | UnicodeString dateString; | |
249 | Calendar * tempCal = cal.clone(); | |
250 | ||
251 | // Set the calendar to now+offset | |
252 | tempCal->setTime(Calendar::getNow(),status); | |
253 | tempCal->add(UCAL_DATE,fDates[n].offset, status); | |
254 | if(U_FAILURE(status)) { | |
255 | pos.setErrorIndex(startIndex); | |
256 | delete tempCal; | |
257 | return; | |
258 | } | |
259 | ||
260 | fDateTimeFormatter->applyPattern(fDatePattern); | |
261 | fDateTimeFormatter->format(*tempCal, dateString, fPos); | |
262 | dateStart = relativeStringOffset; | |
263 | origDateLen = fDates[n].len; | |
264 | modDateLen = dateString.length(); | |
265 | modifiedText.replace(dateStart, origDateLen, dateString); | |
266 | delete tempCal; | |
267 | break; | |
46f4442e | 268 | } |
51004dcb A |
269 | } |
270 | UnicodeString combinedPattern; | |
271 | Formattable timeDatePatterns[] = { fTimePattern, fDatePattern }; | |
272 | fCombinedFormat->format(timeDatePatterns, 2, combinedPattern, fPos, status); // pos is ignored by this | |
273 | fDateTimeFormatter->applyPattern(combinedPattern); | |
274 | fDateTimeFormatter->parse(modifiedText,cal,pos); | |
275 | ||
276 | // Adjust offsets | |
277 | UBool noError = (pos.getErrorIndex() < 0); | |
278 | int32_t offset = (noError)? pos.getIndex(): pos.getErrorIndex(); | |
279 | if (offset >= dateStart + modDateLen) { | |
280 | // offset at or after the end of the replaced text, | |
281 | // correct by the difference between original and replacement | |
282 | offset -= (modDateLen - origDateLen); | |
283 | } else if (offset >= dateStart) { | |
284 | // offset in the replaced text, set it to the beginning of that text | |
285 | // (i.e. the beginning of the relative day string) | |
286 | offset = dateStart; | |
287 | } | |
288 | if (noError) { | |
289 | pos.setIndex(offset); | |
290 | } else { | |
291 | pos.setErrorIndex(offset); | |
46f4442e A |
292 | } |
293 | } | |
46f4442e A |
294 | } |
295 | ||
296 | UDate | |
297 | RelativeDateFormat::parse( const UnicodeString& text, | |
298 | ParsePosition& pos) const { | |
299 | // redefined here because the other parse() function hides this function's | |
300 | // cunterpart on DateFormat | |
301 | return DateFormat::parse(text, pos); | |
302 | } | |
303 | ||
304 | UDate | |
305 | RelativeDateFormat::parse(const UnicodeString& text, UErrorCode& status) const | |
306 | { | |
307 | // redefined here because the other parse() function hides this function's | |
308 | // counterpart on DateFormat | |
309 | return DateFormat::parse(text, status); | |
310 | } | |
311 | ||
312 | ||
313 | const UChar *RelativeDateFormat::getStringForDay(int32_t day, int32_t &len, UErrorCode &status) const { | |
314 | if(U_FAILURE(status)) { | |
315 | return NULL; | |
316 | } | |
317 | ||
318 | // Is it outside the resource bundle's range? | |
319 | if(day < fDayMin || day > fDayMax) { | |
320 | return NULL; // don't have it. | |
321 | } | |
322 | ||
323 | // Linear search the held strings | |
324 | for(int n=0;n<fDatesLen;n++) { | |
325 | if(fDates[n].offset == day) { | |
326 | len = fDates[n].len; | |
327 | return fDates[n].string; | |
328 | } | |
329 | } | |
330 | ||
331 | return NULL; // not found. | |
332 | } | |
333 | ||
334 | UnicodeString& | |
335 | RelativeDateFormat::toPattern(UnicodeString& result, UErrorCode& status) const | |
336 | { | |
337 | if (!U_FAILURE(status)) { | |
338 | result.remove(); | |
51004dcb A |
339 | if (fDatePattern.isEmpty()) { |
340 | result.setTo(fTimePattern); | |
341 | } else if (fTimePattern.isEmpty() || fCombinedFormat == NULL) { | |
342 | result.setTo(fDatePattern); | |
46f4442e | 343 | } else { |
51004dcb A |
344 | Formattable timeDatePatterns[] = { fTimePattern, fDatePattern }; |
345 | FieldPosition pos; | |
346 | fCombinedFormat->format(timeDatePatterns, 2, result, pos, status); | |
46f4442e A |
347 | } |
348 | } | |
349 | return result; | |
350 | } | |
351 | ||
352 | UnicodeString& | |
353 | RelativeDateFormat::toPatternDate(UnicodeString& result, UErrorCode& status) const | |
354 | { | |
355 | if (!U_FAILURE(status)) { | |
356 | result.remove(); | |
51004dcb | 357 | result.setTo(fDatePattern); |
46f4442e A |
358 | } |
359 | return result; | |
360 | } | |
361 | ||
362 | UnicodeString& | |
363 | RelativeDateFormat::toPatternTime(UnicodeString& result, UErrorCode& status) const | |
364 | { | |
365 | if (!U_FAILURE(status)) { | |
366 | result.remove(); | |
51004dcb | 367 | result.setTo(fTimePattern); |
46f4442e A |
368 | } |
369 | return result; | |
370 | } | |
371 | ||
372 | void | |
373 | RelativeDateFormat::applyPatterns(const UnicodeString& datePattern, const UnicodeString& timePattern, UErrorCode &status) | |
374 | { | |
375 | if (!U_FAILURE(status)) { | |
51004dcb A |
376 | fDatePattern.setTo(datePattern); |
377 | fTimePattern.setTo(timePattern); | |
46f4442e A |
378 | } |
379 | } | |
380 | ||
729e4ab9 A |
381 | const DateFormatSymbols* |
382 | RelativeDateFormat::getDateFormatSymbols() const | |
383 | { | |
51004dcb | 384 | return fDateTimeFormatter->getDateFormatSymbols(); |
729e4ab9 A |
385 | } |
386 | ||
46f4442e A |
387 | void RelativeDateFormat::loadDates(UErrorCode &status) { |
388 | CalendarData calData(fLocale, "gregorian", status); | |
389 | ||
390 | UErrorCode tempStatus = status; | |
391 | UResourceBundle *dateTimePatterns = calData.getByKey(DT_DateTimePatternsTag, tempStatus); | |
729e4ab9 A |
392 | if(U_SUCCESS(tempStatus)) { |
393 | int32_t patternsSize = ures_getSize(dateTimePatterns); | |
394 | if (patternsSize > kDateTime) { | |
395 | int32_t resStrLen = 0; | |
396 | ||
397 | int32_t glueIndex = kDateTime; | |
398 | if (patternsSize >= (DateFormat::kDateTimeOffset + DateFormat::kShort + 1)) { | |
399 | // Get proper date time format | |
400 | switch (fDateStyle) { | |
401 | case kFullRelative: | |
402 | case kFull: | |
403 | glueIndex = kDateTimeOffset + kFull; | |
404 | break; | |
405 | case kLongRelative: | |
406 | case kLong: | |
407 | glueIndex = kDateTimeOffset + kLong; | |
408 | break; | |
409 | case kMediumRelative: | |
410 | case kMedium: | |
411 | glueIndex = kDateTimeOffset + kMedium; | |
412 | break; | |
413 | case kShortRelative: | |
414 | case kShort: | |
415 | glueIndex = kDateTimeOffset + kShort; | |
416 | break; | |
417 | default: | |
418 | break; | |
419 | } | |
420 | } | |
421 | ||
422 | const UChar *resStr = ures_getStringByIndex(dateTimePatterns, glueIndex, &resStrLen, &tempStatus); | |
423 | fCombinedFormat = new MessageFormat(UnicodeString(TRUE, resStr, resStrLen), fLocale, tempStatus); | |
424 | } | |
46f4442e A |
425 | } |
426 | ||
51004dcb A |
427 | UResourceBundle *rb = ures_open(NULL, fLocale.getBaseName(), &status); |
428 | UResourceBundle *sb = ures_getByKeyWithFallback(rb, "fields", NULL, &status); | |
429 | rb = ures_getByKeyWithFallback(sb, "day", rb, &status); | |
430 | sb = ures_getByKeyWithFallback(rb, "relative", sb, &status); | |
431 | ures_close(rb); | |
46f4442e A |
432 | // set up min/max |
433 | fDayMin=-1; | |
434 | fDayMax=1; | |
435 | ||
436 | if(U_FAILURE(status)) { | |
437 | fDatesLen=0; | |
51004dcb | 438 | ures_close(sb); |
46f4442e A |
439 | return; |
440 | } | |
441 | ||
51004dcb | 442 | fDatesLen = ures_getSize(sb); |
46f4442e A |
443 | fDates = (URelativeString*) uprv_malloc(sizeof(fDates[0])*fDatesLen); |
444 | ||
445 | // Load in each item into the array... | |
446 | int n = 0; | |
447 | ||
448 | UResourceBundle *subString = NULL; | |
449 | ||
51004dcb A |
450 | while(ures_hasNext(sb) && U_SUCCESS(status)) { // iterate over items |
451 | subString = ures_getNextResource(sb, subString, &status); | |
46f4442e A |
452 | |
453 | if(U_FAILURE(status) || (subString==NULL)) break; | |
454 | ||
455 | // key = offset # | |
456 | const char *key = ures_getKey(subString); | |
457 | ||
458 | // load the string and length | |
459 | int32_t aLen; | |
460 | const UChar* aString = ures_getString(subString, &aLen, &status); | |
461 | ||
462 | if(U_FAILURE(status) || aString == NULL) break; | |
463 | ||
464 | // calculate the offset | |
465 | int32_t offset = atoi(key); | |
466 | ||
467 | // set min/max | |
468 | if(offset < fDayMin) { | |
469 | fDayMin = offset; | |
470 | } | |
471 | if(offset > fDayMax) { | |
472 | fDayMax = offset; | |
473 | } | |
474 | ||
475 | // copy the string pointer | |
476 | fDates[n].offset = offset; | |
477 | fDates[n].string = aString; | |
478 | fDates[n].len = aLen; | |
479 | ||
480 | n++; | |
481 | } | |
482 | ures_close(subString); | |
51004dcb | 483 | ures_close(sb); |
46f4442e A |
484 | |
485 | // the fDates[] array could be sorted here, for direct access. | |
486 | } | |
487 | ||
488 | ||
489 | // this should to be in DateFormat, instead it was copied from SimpleDateFormat. | |
490 | ||
491 | Calendar* | |
492 | RelativeDateFormat::initializeCalendar(TimeZone* adoptZone, const Locale& locale, UErrorCode& status) | |
493 | { | |
494 | if(!U_FAILURE(status)) { | |
495 | fCalendar = Calendar::createInstance(adoptZone?adoptZone:TimeZone::createDefault(), locale, status); | |
496 | } | |
497 | if (U_SUCCESS(status) && fCalendar == NULL) { | |
498 | status = U_MEMORY_ALLOCATION_ERROR; | |
499 | } | |
500 | return fCalendar; | |
501 | } | |
502 | ||
503 | int32_t RelativeDateFormat::dayDifference(Calendar &cal, UErrorCode &status) { | |
504 | if(U_FAILURE(status)) { | |
505 | return 0; | |
506 | } | |
507 | // TODO: Cache the nowCal to avoid heap allocs? Would be difficult, don't know the calendar type | |
508 | Calendar *nowCal = cal.clone(); | |
509 | nowCal->setTime(Calendar::getNow(), status); | |
510 | ||
511 | // For the day difference, we are interested in the difference in the (modified) julian day number | |
512 | // which is midnight to midnight. Using fieldDifference() is NOT correct here, because | |
513 | // 6pm Jan 4th to 10am Jan 5th should be considered "tomorrow". | |
514 | int32_t dayDiff = cal.get(UCAL_JULIAN_DAY, status) - nowCal->get(UCAL_JULIAN_DAY, status); | |
515 | ||
516 | delete nowCal; | |
517 | return dayDiff; | |
518 | } | |
519 | ||
520 | U_NAMESPACE_END | |
521 | ||
522 | #endif | |
523 |