]>
Commit | Line | Data |
---|---|---|
1 | // © 2016 and later: Unicode, Inc. and others. | |
2 | // License & terms of use: http://www.unicode.org/copyright.html | |
3 | /* | |
4 | ******************************************************************************* | |
5 | * Copyright (C) 2007-2016, International Business Machines Corporation and | |
6 | * others. All Rights Reserved. | |
7 | ******************************************************************************* | |
8 | */ | |
9 | ||
10 | #include "unicode/utypes.h" | |
11 | ||
12 | #if !UCONFIG_NO_FORMATTING | |
13 | ||
14 | #include <stdlib.h> | |
15 | ||
16 | #include "unicode/datefmt.h" | |
17 | #include "unicode/reldatefmt.h" | |
18 | #include "unicode/simpleformatter.h" | |
19 | #include "unicode/smpdtfmt.h" | |
20 | #include "unicode/udisplaycontext.h" | |
21 | #include "unicode/uchar.h" | |
22 | #include "unicode/brkiter.h" | |
23 | #include "unicode/ucasemap.h" | |
24 | #include "reldtfmt.h" | |
25 | #include "cmemory.h" | |
26 | #include "uresimp.h" | |
27 | ||
28 | U_NAMESPACE_BEGIN | |
29 | ||
30 | ||
31 | /** | |
32 | * An array of URelativeString structs is used to store the resource data loaded out of the bundle. | |
33 | */ | |
34 | struct URelativeString { | |
35 | int32_t offset; /** offset of this item, such as, the relative date **/ | |
36 | int32_t len; /** length of the string **/ | |
37 | const UChar* string; /** string, or NULL if not set **/ | |
38 | }; | |
39 | ||
40 | UOBJECT_DEFINE_RTTI_IMPLEMENTATION(RelativeDateFormat) | |
41 | ||
42 | RelativeDateFormat::RelativeDateFormat(const RelativeDateFormat& other) : | |
43 | DateFormat(other), fDateTimeFormatter(NULL), fDatePattern(other.fDatePattern), | |
44 | fTimePattern(other.fTimePattern), fCombinedFormat(NULL), | |
45 | fDateStyle(other.fDateStyle), fLocale(other.fLocale), | |
46 | fDatesLen(other.fDatesLen), fDates(NULL), | |
47 | fCombinedHasDateAtStart(other.fCombinedHasDateAtStart), | |
48 | fCapitalizationInfoSet(other.fCapitalizationInfoSet), | |
49 | fCapitalizationOfRelativeUnitsForUIListMenu(other.fCapitalizationOfRelativeUnitsForUIListMenu), | |
50 | fCapitalizationOfRelativeUnitsForStandAlone(other.fCapitalizationOfRelativeUnitsForStandAlone), | |
51 | fCapitalizationBrkIter(NULL) | |
52 | { | |
53 | if(other.fDateTimeFormatter != NULL) { | |
54 | fDateTimeFormatter = (SimpleDateFormat*)other.fDateTimeFormatter->clone(); | |
55 | } | |
56 | if(other.fCombinedFormat != NULL) { | |
57 | fCombinedFormat = new SimpleFormatter(*other.fCombinedFormat); | |
58 | } | |
59 | if (fDatesLen > 0) { | |
60 | fDates = (URelativeString*) uprv_malloc(sizeof(fDates[0])*(size_t)fDatesLen); | |
61 | uprv_memcpy(fDates, other.fDates, sizeof(fDates[0])*(size_t)fDatesLen); | |
62 | } | |
63 | #if !UCONFIG_NO_BREAK_ITERATION | |
64 | if (other.fCapitalizationBrkIter != NULL) { | |
65 | fCapitalizationBrkIter = (other.fCapitalizationBrkIter)->clone(); | |
66 | } | |
67 | #endif | |
68 | } | |
69 | ||
70 | RelativeDateFormat::RelativeDateFormat( UDateFormatStyle timeStyle, UDateFormatStyle dateStyle, | |
71 | const Locale& locale, UErrorCode& status) : | |
72 | DateFormat(), fDateTimeFormatter(NULL), fDatePattern(), fTimePattern(), fCombinedFormat(NULL), | |
73 | fDateStyle(dateStyle), fLocale(locale), fDatesLen(0), fDates(NULL), | |
74 | fCombinedHasDateAtStart(FALSE), fCapitalizationInfoSet(FALSE), | |
75 | fCapitalizationOfRelativeUnitsForUIListMenu(FALSE), fCapitalizationOfRelativeUnitsForStandAlone(FALSE), | |
76 | fCapitalizationBrkIter(NULL) | |
77 | { | |
78 | if(U_FAILURE(status) ) { | |
79 | return; | |
80 | } | |
81 | ||
82 | if (timeStyle < UDAT_NONE || timeStyle > UDAT_SHORT) { | |
83 | // don't support other time styles (e.g. relative styles), for now | |
84 | status = U_ILLEGAL_ARGUMENT_ERROR; | |
85 | return; | |
86 | } | |
87 | UDateFormatStyle baseDateStyle = (dateStyle > UDAT_SHORT)? (UDateFormatStyle)(dateStyle & ~UDAT_RELATIVE): dateStyle; | |
88 | DateFormat * df; | |
89 | // Get fDateTimeFormatter from either date or time style (does not matter, we will override the pattern). | |
90 | // We do need to get separate patterns for the date & time styles. | |
91 | if (baseDateStyle != UDAT_NONE) { | |
92 | df = createDateInstance((EStyle)baseDateStyle, locale); | |
93 | fDateTimeFormatter=dynamic_cast<SimpleDateFormat *>(df); | |
94 | if (fDateTimeFormatter == NULL) { | |
95 | status = U_UNSUPPORTED_ERROR; | |
96 | return; | |
97 | } | |
98 | fDateTimeFormatter->toPattern(fDatePattern); | |
99 | if (timeStyle != UDAT_NONE) { | |
100 | df = createTimeInstance((EStyle)timeStyle, locale); | |
101 | SimpleDateFormat *sdf = dynamic_cast<SimpleDateFormat *>(df); | |
102 | if (sdf != NULL) { | |
103 | sdf->toPattern(fTimePattern); | |
104 | delete sdf; | |
105 | } | |
106 | } | |
107 | } else { | |
108 | // does not matter whether timeStyle is UDAT_NONE, we need something for fDateTimeFormatter | |
109 | df = createTimeInstance((EStyle)timeStyle, locale); | |
110 | fDateTimeFormatter=dynamic_cast<SimpleDateFormat *>(df); | |
111 | if (fDateTimeFormatter == NULL) { | |
112 | status = U_UNSUPPORTED_ERROR; | |
113 | delete df; | |
114 | return; | |
115 | } | |
116 | fDateTimeFormatter->toPattern(fTimePattern); | |
117 | } | |
118 | ||
119 | // Initialize the parent fCalendar, so that parse() works correctly. | |
120 | initializeCalendar(NULL, locale, status); | |
121 | loadDates(status); | |
122 | } | |
123 | ||
124 | RelativeDateFormat::~RelativeDateFormat() { | |
125 | delete fDateTimeFormatter; | |
126 | delete fCombinedFormat; | |
127 | uprv_free(fDates); | |
128 | #if !UCONFIG_NO_BREAK_ITERATION | |
129 | delete fCapitalizationBrkIter; | |
130 | #endif | |
131 | } | |
132 | ||
133 | ||
134 | Format* RelativeDateFormat::clone(void) const { | |
135 | return new RelativeDateFormat(*this); | |
136 | } | |
137 | ||
138 | UBool RelativeDateFormat::operator==(const Format& other) const { | |
139 | if(DateFormat::operator==(other)) { | |
140 | // The DateFormat::operator== check for fCapitalizationContext equality above | |
141 | // is sufficient to check equality of all derived context-related data. | |
142 | // DateFormat::operator== guarantees following cast is safe | |
143 | RelativeDateFormat* that = (RelativeDateFormat*)&other; | |
144 | return (fDateStyle==that->fDateStyle && | |
145 | fDatePattern==that->fDatePattern && | |
146 | fTimePattern==that->fTimePattern && | |
147 | fLocale==that->fLocale ); | |
148 | } | |
149 | return FALSE; | |
150 | } | |
151 | ||
152 | static const UChar APOSTROPHE = (UChar)0x0027; | |
153 | ||
154 | UnicodeString& RelativeDateFormat::format( Calendar& cal, | |
155 | UnicodeString& appendTo, | |
156 | FieldPosition& pos) const { | |
157 | ||
158 | UErrorCode status = U_ZERO_ERROR; | |
159 | UnicodeString relativeDayString; | |
160 | UDisplayContext capitalizationContext = getContext(UDISPCTX_TYPE_CAPITALIZATION, status); | |
161 | ||
162 | // calculate the difference, in days, between 'cal' and now. | |
163 | int dayDiff = dayDifference(cal, status); | |
164 | ||
165 | // look up string | |
166 | int32_t len = 0; | |
167 | const UChar *theString = getStringForDay(dayDiff, len, status); | |
168 | if(U_SUCCESS(status) && (theString!=NULL)) { | |
169 | // found a relative string | |
170 | relativeDayString.setTo(theString, len); | |
171 | } | |
172 | ||
173 | if ( relativeDayString.length() > 0 && !fDatePattern.isEmpty() && | |
174 | (fTimePattern.isEmpty() || fCombinedFormat == NULL || fCombinedHasDateAtStart)) { | |
175 | #if !UCONFIG_NO_BREAK_ITERATION | |
176 | // capitalize relativeDayString according to context for relative, set formatter no context | |
177 | if ( u_islower(relativeDayString.char32At(0)) && fCapitalizationBrkIter!= NULL && | |
178 | ( capitalizationContext==UDISPCTX_CAPITALIZATION_FOR_BEGINNING_OF_SENTENCE || | |
179 | (capitalizationContext==UDISPCTX_CAPITALIZATION_FOR_UI_LIST_OR_MENU && fCapitalizationOfRelativeUnitsForUIListMenu) || | |
180 | (capitalizationContext==UDISPCTX_CAPITALIZATION_FOR_STANDALONE && fCapitalizationOfRelativeUnitsForStandAlone) ) ) { | |
181 | // titlecase first word of relativeDayString | |
182 | relativeDayString.toTitle(fCapitalizationBrkIter, fLocale, U_TITLECASE_NO_LOWERCASE | U_TITLECASE_NO_BREAK_ADJUSTMENT); | |
183 | } | |
184 | #endif | |
185 | fDateTimeFormatter->setContext(UDISPCTX_CAPITALIZATION_NONE, status); | |
186 | } else { | |
187 | // set our context for the formatter | |
188 | fDateTimeFormatter->setContext(capitalizationContext, status); | |
189 | } | |
190 | ||
191 | if (fDatePattern.isEmpty()) { | |
192 | fDateTimeFormatter->applyPattern(fTimePattern); | |
193 | fDateTimeFormatter->format(cal,appendTo,pos); | |
194 | } else if (fTimePattern.isEmpty() || fCombinedFormat == NULL) { | |
195 | if (relativeDayString.length() > 0) { | |
196 | appendTo.append(relativeDayString); | |
197 | } else { | |
198 | fDateTimeFormatter->applyPattern(fDatePattern); | |
199 | fDateTimeFormatter->format(cal,appendTo,pos); | |
200 | } | |
201 | } else { | |
202 | UnicodeString datePattern; | |
203 | if (relativeDayString.length() > 0) { | |
204 | // Need to quote the relativeDayString to make it a legal date pattern | |
205 | relativeDayString.findAndReplace(UNICODE_STRING("'", 1), UNICODE_STRING("''", 2)); // double any existing APOSTROPHE | |
206 | relativeDayString.insert(0, APOSTROPHE); // add APOSTROPHE at beginning... | |
207 | relativeDayString.append(APOSTROPHE); // and at end | |
208 | datePattern.setTo(relativeDayString); | |
209 | } else { | |
210 | datePattern.setTo(fDatePattern); | |
211 | } | |
212 | UnicodeString combinedPattern; | |
213 | fCombinedFormat->format(fTimePattern, datePattern, combinedPattern, status); | |
214 | fDateTimeFormatter->applyPattern(combinedPattern); | |
215 | fDateTimeFormatter->format(cal,appendTo,pos); | |
216 | } | |
217 | ||
218 | return appendTo; | |
219 | } | |
220 | ||
221 | ||
222 | ||
223 | UnicodeString& | |
224 | RelativeDateFormat::format(const Formattable& obj, | |
225 | UnicodeString& appendTo, | |
226 | FieldPosition& pos, | |
227 | UErrorCode& status) const | |
228 | { | |
229 | // this is just here to get around the hiding problem | |
230 | // (the previous format() override would hide the version of | |
231 | // format() on DateFormat that this function correspond to, so we | |
232 | // have to redefine it here) | |
233 | return DateFormat::format(obj, appendTo, pos, status); | |
234 | } | |
235 | ||
236 | ||
237 | void RelativeDateFormat::parse( const UnicodeString& text, | |
238 | Calendar& cal, | |
239 | ParsePosition& pos) const { | |
240 | ||
241 | int32_t startIndex = pos.getIndex(); | |
242 | if (fDatePattern.isEmpty()) { | |
243 | // no date pattern, try parsing as time | |
244 | fDateTimeFormatter->applyPattern(fTimePattern); | |
245 | fDateTimeFormatter->parse(text,cal,pos); | |
246 | } else if (fTimePattern.isEmpty() || fCombinedFormat == NULL) { | |
247 | // no time pattern or way to combine, try parsing as date | |
248 | // first check whether text matches a relativeDayString | |
249 | UBool matchedRelative = FALSE; | |
250 | for (int n=0; n < fDatesLen && !matchedRelative; n++) { | |
251 | if (fDates[n].string != NULL && | |
252 | text.compare(startIndex, fDates[n].len, fDates[n].string) == 0) { | |
253 | // it matched, handle the relative day string | |
254 | UErrorCode status = U_ZERO_ERROR; | |
255 | matchedRelative = TRUE; | |
256 | ||
257 | // Set the calendar to now+offset | |
258 | cal.setTime(Calendar::getNow(),status); | |
259 | cal.add(UCAL_DATE,fDates[n].offset, status); | |
260 | ||
261 | if(U_FAILURE(status)) { | |
262 | // failure in setting calendar field, set offset to beginning of rel day string | |
263 | pos.setErrorIndex(startIndex); | |
264 | } else { | |
265 | pos.setIndex(startIndex + fDates[n].len); | |
266 | } | |
267 | } | |
268 | } | |
269 | if (!matchedRelative) { | |
270 | // just parse as normal date | |
271 | fDateTimeFormatter->applyPattern(fDatePattern); | |
272 | fDateTimeFormatter->parse(text,cal,pos); | |
273 | } | |
274 | } else { | |
275 | // Here we replace any relativeDayString in text with the equivalent date | |
276 | // formatted per fDatePattern, then parse text normally using the combined pattern. | |
277 | UnicodeString modifiedText(text); | |
278 | FieldPosition fPos; | |
279 | int32_t dateStart = 0, origDateLen = 0, modDateLen = 0; | |
280 | UErrorCode status = U_ZERO_ERROR; | |
281 | for (int n=0; n < fDatesLen; n++) { | |
282 | int32_t relativeStringOffset; | |
283 | if (fDates[n].string != NULL && | |
284 | (relativeStringOffset = modifiedText.indexOf(fDates[n].string, fDates[n].len, startIndex)) >= startIndex) { | |
285 | // it matched, replace the relative date with a real one for parsing | |
286 | UnicodeString dateString; | |
287 | Calendar * tempCal = cal.clone(); | |
288 | ||
289 | // Set the calendar to now+offset | |
290 | tempCal->setTime(Calendar::getNow(),status); | |
291 | tempCal->add(UCAL_DATE,fDates[n].offset, status); | |
292 | if(U_FAILURE(status)) { | |
293 | pos.setErrorIndex(startIndex); | |
294 | delete tempCal; | |
295 | return; | |
296 | } | |
297 | ||
298 | fDateTimeFormatter->applyPattern(fDatePattern); | |
299 | fDateTimeFormatter->format(*tempCal, dateString, fPos); | |
300 | dateStart = relativeStringOffset; | |
301 | origDateLen = fDates[n].len; | |
302 | modDateLen = dateString.length(); | |
303 | modifiedText.replace(dateStart, origDateLen, dateString); | |
304 | delete tempCal; | |
305 | break; | |
306 | } | |
307 | } | |
308 | UnicodeString combinedPattern; | |
309 | fCombinedFormat->format(fTimePattern, fDatePattern, combinedPattern, status); | |
310 | fDateTimeFormatter->applyPattern(combinedPattern); | |
311 | fDateTimeFormatter->parse(modifiedText,cal,pos); | |
312 | ||
313 | // Adjust offsets | |
314 | UBool noError = (pos.getErrorIndex() < 0); | |
315 | int32_t offset = (noError)? pos.getIndex(): pos.getErrorIndex(); | |
316 | if (offset >= dateStart + modDateLen) { | |
317 | // offset at or after the end of the replaced text, | |
318 | // correct by the difference between original and replacement | |
319 | offset -= (modDateLen - origDateLen); | |
320 | } else if (offset >= dateStart) { | |
321 | // offset in the replaced text, set it to the beginning of that text | |
322 | // (i.e. the beginning of the relative day string) | |
323 | offset = dateStart; | |
324 | } | |
325 | if (noError) { | |
326 | pos.setIndex(offset); | |
327 | } else { | |
328 | pos.setErrorIndex(offset); | |
329 | } | |
330 | } | |
331 | } | |
332 | ||
333 | UDate | |
334 | RelativeDateFormat::parse( const UnicodeString& text, | |
335 | ParsePosition& pos) const { | |
336 | // redefined here because the other parse() function hides this function's | |
337 | // cunterpart on DateFormat | |
338 | return DateFormat::parse(text, pos); | |
339 | } | |
340 | ||
341 | UDate | |
342 | RelativeDateFormat::parse(const UnicodeString& text, UErrorCode& status) const | |
343 | { | |
344 | // redefined here because the other parse() function hides this function's | |
345 | // counterpart on DateFormat | |
346 | return DateFormat::parse(text, status); | |
347 | } | |
348 | ||
349 | ||
350 | const UChar *RelativeDateFormat::getStringForDay(int32_t day, int32_t &len, UErrorCode &status) const { | |
351 | if(U_FAILURE(status)) { | |
352 | return NULL; | |
353 | } | |
354 | ||
355 | // Is it inside the resource bundle's range? | |
356 | int n = day + UDAT_DIRECTION_THIS; | |
357 | if (n >= 0 && n < fDatesLen) { | |
358 | if (fDates[n].offset == day && fDates[n].string != NULL) { | |
359 | len = fDates[n].len; | |
360 | return fDates[n].string; | |
361 | } | |
362 | } | |
363 | return NULL; // not found. | |
364 | } | |
365 | ||
366 | UnicodeString& | |
367 | RelativeDateFormat::toPattern(UnicodeString& result, UErrorCode& status) const | |
368 | { | |
369 | if (!U_FAILURE(status)) { | |
370 | result.remove(); | |
371 | if (fDatePattern.isEmpty()) { | |
372 | result.setTo(fTimePattern); | |
373 | } else if (fTimePattern.isEmpty() || fCombinedFormat == NULL) { | |
374 | result.setTo(fDatePattern); | |
375 | } else { | |
376 | fCombinedFormat->format(fTimePattern, fDatePattern, result, status); | |
377 | } | |
378 | } | |
379 | return result; | |
380 | } | |
381 | ||
382 | UnicodeString& | |
383 | RelativeDateFormat::toPatternDate(UnicodeString& result, UErrorCode& status) const | |
384 | { | |
385 | if (!U_FAILURE(status)) { | |
386 | result.remove(); | |
387 | result.setTo(fDatePattern); | |
388 | } | |
389 | return result; | |
390 | } | |
391 | ||
392 | UnicodeString& | |
393 | RelativeDateFormat::toPatternTime(UnicodeString& result, UErrorCode& status) const | |
394 | { | |
395 | if (!U_FAILURE(status)) { | |
396 | result.remove(); | |
397 | result.setTo(fTimePattern); | |
398 | } | |
399 | return result; | |
400 | } | |
401 | ||
402 | void | |
403 | RelativeDateFormat::applyPatterns(const UnicodeString& datePattern, const UnicodeString& timePattern, UErrorCode &status) | |
404 | { | |
405 | if (!U_FAILURE(status)) { | |
406 | fDatePattern.setTo(datePattern); | |
407 | fTimePattern.setTo(timePattern); | |
408 | } | |
409 | } | |
410 | ||
411 | const DateFormatSymbols* | |
412 | RelativeDateFormat::getDateFormatSymbols() const | |
413 | { | |
414 | return fDateTimeFormatter->getDateFormatSymbols(); | |
415 | } | |
416 | ||
417 | // override the DateFormat implementation in order to | |
418 | // lazily initialize relevant items | |
419 | void | |
420 | RelativeDateFormat::setContext(UDisplayContext value, UErrorCode& status) | |
421 | { | |
422 | DateFormat::setContext(value, status); | |
423 | if (U_SUCCESS(status)) { | |
424 | if (!fCapitalizationInfoSet && | |
425 | (value==UDISPCTX_CAPITALIZATION_FOR_UI_LIST_OR_MENU || value==UDISPCTX_CAPITALIZATION_FOR_STANDALONE)) { | |
426 | initCapitalizationContextInfo(fLocale); | |
427 | fCapitalizationInfoSet = TRUE; | |
428 | } | |
429 | #if !UCONFIG_NO_BREAK_ITERATION | |
430 | if ( fCapitalizationBrkIter == NULL && (value==UDISPCTX_CAPITALIZATION_FOR_BEGINNING_OF_SENTENCE || | |
431 | (value==UDISPCTX_CAPITALIZATION_FOR_UI_LIST_OR_MENU && fCapitalizationOfRelativeUnitsForUIListMenu) || | |
432 | (value==UDISPCTX_CAPITALIZATION_FOR_STANDALONE && fCapitalizationOfRelativeUnitsForStandAlone)) ) { | |
433 | status = U_ZERO_ERROR; | |
434 | fCapitalizationBrkIter = BreakIterator::createSentenceInstance(fLocale, status); | |
435 | if (U_FAILURE(status)) { | |
436 | delete fCapitalizationBrkIter; | |
437 | fCapitalizationBrkIter = NULL; | |
438 | } | |
439 | } | |
440 | #endif | |
441 | } | |
442 | } | |
443 | ||
444 | void | |
445 | RelativeDateFormat::initCapitalizationContextInfo(const Locale& thelocale) | |
446 | { | |
447 | #if !UCONFIG_NO_BREAK_ITERATION | |
448 | const char * localeID = (thelocale != NULL)? thelocale.getBaseName(): NULL; | |
449 | UErrorCode status = U_ZERO_ERROR; | |
450 | LocalUResourceBundlePointer rb(ures_open(NULL, localeID, &status)); | |
451 | ures_getByKeyWithFallback(rb.getAlias(), | |
452 | "contextTransforms/relative", | |
453 | rb.getAlias(), &status); | |
454 | if (U_SUCCESS(status) && rb != NULL) { | |
455 | int32_t len = 0; | |
456 | const int32_t * intVector = ures_getIntVector(rb.getAlias(), | |
457 | &len, &status); | |
458 | if (U_SUCCESS(status) && intVector != NULL && len >= 2) { | |
459 | fCapitalizationOfRelativeUnitsForUIListMenu = static_cast<UBool>(intVector[0]); | |
460 | fCapitalizationOfRelativeUnitsForStandAlone = static_cast<UBool>(intVector[1]); | |
461 | } | |
462 | } | |
463 | #endif | |
464 | } | |
465 | ||
466 | namespace { | |
467 | ||
468 | /** | |
469 | * Sink for getting data from fields/day/relative data. | |
470 | * For loading relative day names, e.g., "yesterday", "today". | |
471 | */ | |
472 | ||
473 | struct RelDateFmtDataSink : public ResourceSink { | |
474 | URelativeString *fDatesPtr; | |
475 | int32_t fDatesLen; | |
476 | ||
477 | RelDateFmtDataSink(URelativeString* fDates, int32_t len) : fDatesPtr(fDates), fDatesLen(len) { | |
478 | for (int32_t i = 0; i < fDatesLen; ++i) { | |
479 | fDatesPtr[i].offset = 0; | |
480 | fDatesPtr[i].string = NULL; | |
481 | fDatesPtr[i].len = -1; | |
482 | } | |
483 | } | |
484 | ||
485 | virtual ~RelDateFmtDataSink(); | |
486 | ||
487 | virtual void put(const char *key, ResourceValue &value, | |
488 | UBool /*noFallback*/, UErrorCode &errorCode) { | |
489 | ResourceTable relDayTable = value.getTable(errorCode); | |
490 | int32_t n = 0; | |
491 | int32_t len = 0; | |
492 | for (int32_t i = 0; relDayTable.getKeyAndValue(i, key, value); ++i) { | |
493 | // Find the relative offset. | |
494 | int32_t offset = atoi(key); | |
495 | ||
496 | // Put in the proper spot, but don't override existing data. | |
497 | n = offset + UDAT_DIRECTION_THIS; // Converts to index in UDAT_R | |
498 | if (n < fDatesLen && fDatesPtr[n].string == NULL) { | |
499 | // Not found and n is an empty slot. | |
500 | fDatesPtr[n].offset = offset; | |
501 | fDatesPtr[n].string = value.getString(len, errorCode); | |
502 | fDatesPtr[n].len = len; | |
503 | } | |
504 | } | |
505 | } | |
506 | }; | |
507 | ||
508 | ||
509 | // Virtual destructors must be defined out of line. | |
510 | RelDateFmtDataSink::~RelDateFmtDataSink() {} | |
511 | ||
512 | } // Namespace | |
513 | ||
514 | ||
515 | static const UChar patItem1[] = {0x7B,0x31,0x7D}; // "{1}" | |
516 | static const int32_t patItem1Len = 3; | |
517 | ||
518 | void RelativeDateFormat::loadDates(UErrorCode &status) { | |
519 | UResourceBundle *rb = ures_open(NULL, fLocale.getBaseName(), &status); | |
520 | LocalUResourceBundlePointer dateTimePatterns( | |
521 | ures_getByKeyWithFallback(rb, | |
522 | "calendar/gregorian/DateTimePatterns", | |
523 | (UResourceBundle*)NULL, &status)); | |
524 | if(U_SUCCESS(status)) { | |
525 | int32_t patternsSize = ures_getSize(dateTimePatterns.getAlias()); | |
526 | if (patternsSize > kDateTime) { | |
527 | int32_t resStrLen = 0; | |
528 | int32_t glueIndex = kDateTime; | |
529 | if (patternsSize >= (kDateTimeOffset + kShort + 1)) { | |
530 | int32_t offsetIncrement = (fDateStyle & ~kRelative); // Remove relative bit. | |
531 | if (offsetIncrement >= (int32_t)kFull && | |
532 | offsetIncrement <= (int32_t)kShortRelative) { | |
533 | glueIndex = kDateTimeOffset + offsetIncrement; | |
534 | } | |
535 | } | |
536 | ||
537 | const UChar *resStr = ures_getStringByIndex(dateTimePatterns.getAlias(), glueIndex, &resStrLen, &status); | |
538 | if (U_SUCCESS(status) && resStrLen >= patItem1Len && u_strncmp(resStr,patItem1,patItem1Len)==0) { | |
539 | fCombinedHasDateAtStart = TRUE; | |
540 | } | |
541 | fCombinedFormat = new SimpleFormatter(UnicodeString(TRUE, resStr, resStrLen), 2, 2, status); | |
542 | } | |
543 | } | |
544 | ||
545 | // Data loading for relative names, e.g., "yesterday", "today", "tomorrow". | |
546 | fDatesLen = UDAT_DIRECTION_COUNT; // Maximum defined by data. | |
547 | fDates = (URelativeString*) uprv_malloc(sizeof(fDates[0])*fDatesLen); | |
548 | ||
549 | RelDateFmtDataSink sink(fDates, fDatesLen); | |
550 | ures_getAllItemsWithFallback(rb, "fields/day/relative", sink, status); | |
551 | ||
552 | ures_close(rb); | |
553 | ||
554 | if(U_FAILURE(status)) { | |
555 | fDatesLen=0; | |
556 | return; | |
557 | } | |
558 | } | |
559 | ||
560 | //---------------------------------------------------------------------- | |
561 | ||
562 | // this should to be in DateFormat, instead it was copied from SimpleDateFormat. | |
563 | ||
564 | Calendar* | |
565 | RelativeDateFormat::initializeCalendar(TimeZone* adoptZone, const Locale& locale, UErrorCode& status) | |
566 | { | |
567 | if(!U_FAILURE(status)) { | |
568 | fCalendar = Calendar::createInstance(adoptZone?adoptZone:TimeZone::createDefault(), locale, status); | |
569 | } | |
570 | if (U_SUCCESS(status) && fCalendar == NULL) { | |
571 | status = U_MEMORY_ALLOCATION_ERROR; | |
572 | } | |
573 | return fCalendar; | |
574 | } | |
575 | ||
576 | int32_t RelativeDateFormat::dayDifference(Calendar &cal, UErrorCode &status) { | |
577 | if(U_FAILURE(status)) { | |
578 | return 0; | |
579 | } | |
580 | // TODO: Cache the nowCal to avoid heap allocs? Would be difficult, don't know the calendar type | |
581 | Calendar *nowCal = cal.clone(); | |
582 | nowCal->setTime(Calendar::getNow(), status); | |
583 | ||
584 | // For the day difference, we are interested in the difference in the (modified) julian day number | |
585 | // which is midnight to midnight. Using fieldDifference() is NOT correct here, because | |
586 | // 6pm Jan 4th to 10am Jan 5th should be considered "tomorrow". | |
587 | int32_t dayDiff = cal.get(UCAL_JULIAN_DAY, status) - nowCal->get(UCAL_JULIAN_DAY, status); | |
588 | ||
589 | delete nowCal; | |
590 | return dayDiff; | |
591 | } | |
592 | ||
593 | U_NAMESPACE_END | |
594 | ||
595 | #endif /* !UCONFIG_NO_FORMATTING */ |