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