/src/mozilla-central/intl/icu/source/i18n/dtitvfmt.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 | | * Copyright (C) 2008-2016, International Business Machines Corporation and |
5 | | * others. All Rights Reserved. |
6 | | ******************************************************************************* |
7 | | * |
8 | | * File DTITVFMT.CPP |
9 | | * |
10 | | ******************************************************************************* |
11 | | */ |
12 | | |
13 | | #include "utypeinfo.h" // for 'typeid' to work |
14 | | |
15 | | #include "unicode/dtitvfmt.h" |
16 | | |
17 | | #if !UCONFIG_NO_FORMATTING |
18 | | |
19 | | //TODO: put in compilation |
20 | | //#define DTITVFMT_DEBUG 1 |
21 | | |
22 | | #include "unicode/calendar.h" |
23 | | #include "unicode/dtptngen.h" |
24 | | #include "unicode/dtitvinf.h" |
25 | | #include "unicode/simpleformatter.h" |
26 | | #include "cmemory.h" |
27 | | #include "cstring.h" |
28 | | #include "dtitv_impl.h" |
29 | | #include "mutex.h" |
30 | | #include "uresimp.h" |
31 | | |
32 | | #ifdef DTITVFMT_DEBUG |
33 | | #include <iostream> |
34 | | #endif |
35 | | |
36 | | U_NAMESPACE_BEGIN |
37 | | |
38 | | |
39 | | |
40 | | #ifdef DTITVFMT_DEBUG |
41 | | #define PRINTMESG(msg) { std::cout << "(" << __FILE__ << ":" << __LINE__ << ") " << msg << "\n"; } |
42 | | #endif |
43 | | |
44 | | |
45 | | static const UChar gDateFormatSkeleton[][11] = { |
46 | | //yMMMMEEEEd |
47 | | {LOW_Y, CAP_M, CAP_M, CAP_M, CAP_M, CAP_E, CAP_E, CAP_E, CAP_E, LOW_D, 0}, |
48 | | //yMMMMd |
49 | | {LOW_Y, CAP_M, CAP_M, CAP_M, CAP_M, LOW_D, 0}, |
50 | | //yMMMd |
51 | | {LOW_Y, CAP_M, CAP_M, CAP_M, LOW_D, 0}, |
52 | | //yMd |
53 | | {LOW_Y, CAP_M, LOW_D, 0} }; |
54 | | |
55 | | |
56 | | static const char gCalendarTag[] = "calendar"; |
57 | | static const char gGregorianTag[] = "gregorian"; |
58 | | static const char gDateTimePatternsTag[] = "DateTimePatterns"; |
59 | | |
60 | | |
61 | | // latestFirst: |
62 | | static const UChar gLaterFirstPrefix[] = {LOW_L, LOW_A, LOW_T, LOW_E, LOW_S,LOW_T, CAP_F, LOW_I, LOW_R, LOW_S, LOW_T, COLON}; |
63 | | |
64 | | // earliestFirst: |
65 | | static const UChar gEarlierFirstPrefix[] = {LOW_E, LOW_A, LOW_R, LOW_L, LOW_I, LOW_E, LOW_S, LOW_T, CAP_F, LOW_I, LOW_R, LOW_S, LOW_T, COLON}; |
66 | | |
67 | | |
68 | | UOBJECT_DEFINE_RTTI_IMPLEMENTATION(DateIntervalFormat) |
69 | | |
70 | | // Mutex, protects access to fDateFormat, fFromCalendar and fToCalendar. |
71 | | // Needed because these data members are modified by const methods of DateIntervalFormat. |
72 | | |
73 | | static UMutex gFormatterMutex = U_MUTEX_INITIALIZER; |
74 | | |
75 | | DateIntervalFormat* U_EXPORT2 |
76 | | DateIntervalFormat::createInstance(const UnicodeString& skeleton, |
77 | 0 | UErrorCode& status) { |
78 | 0 | return createInstance(skeleton, Locale::getDefault(), status); |
79 | 0 | } |
80 | | |
81 | | |
82 | | DateIntervalFormat* U_EXPORT2 |
83 | | DateIntervalFormat::createInstance(const UnicodeString& skeleton, |
84 | | const Locale& locale, |
85 | 0 | UErrorCode& status) { |
86 | | #ifdef DTITVFMT_DEBUG |
87 | | char result[1000]; |
88 | | char result_1[1000]; |
89 | | char mesg[2000]; |
90 | | skeleton.extract(0, skeleton.length(), result, "UTF-8"); |
91 | | UnicodeString pat; |
92 | | ((SimpleDateFormat*)dtfmt)->toPattern(pat); |
93 | | pat.extract(0, pat.length(), result_1, "UTF-8"); |
94 | | sprintf(mesg, "skeleton: %s; pattern: %s\n", result, result_1); |
95 | | PRINTMESG(mesg) |
96 | | #endif |
97 | |
|
98 | 0 | DateIntervalInfo* dtitvinf = new DateIntervalInfo(locale, status); |
99 | 0 | return create(locale, dtitvinf, &skeleton, status); |
100 | 0 | } |
101 | | |
102 | | |
103 | | |
104 | | DateIntervalFormat* U_EXPORT2 |
105 | | DateIntervalFormat::createInstance(const UnicodeString& skeleton, |
106 | | const DateIntervalInfo& dtitvinf, |
107 | 0 | UErrorCode& status) { |
108 | 0 | return createInstance(skeleton, Locale::getDefault(), dtitvinf, status); |
109 | 0 | } |
110 | | |
111 | | |
112 | | DateIntervalFormat* U_EXPORT2 |
113 | | DateIntervalFormat::createInstance(const UnicodeString& skeleton, |
114 | | const Locale& locale, |
115 | | const DateIntervalInfo& dtitvinf, |
116 | 0 | UErrorCode& status) { |
117 | 0 | DateIntervalInfo* ptn = dtitvinf.clone(); |
118 | 0 | return create(locale, ptn, &skeleton, status); |
119 | 0 | } |
120 | | |
121 | | |
122 | | DateIntervalFormat::DateIntervalFormat() |
123 | | : fInfo(NULL), |
124 | | fDateFormat(NULL), |
125 | | fFromCalendar(NULL), |
126 | | fToCalendar(NULL), |
127 | | fLocale(Locale::getRoot()), |
128 | | fDatePattern(NULL), |
129 | | fTimePattern(NULL), |
130 | | fDateTimeFormat(NULL) |
131 | 0 | {} |
132 | | |
133 | | |
134 | | DateIntervalFormat::DateIntervalFormat(const DateIntervalFormat& itvfmt) |
135 | | : Format(itvfmt), |
136 | | fInfo(NULL), |
137 | | fDateFormat(NULL), |
138 | | fFromCalendar(NULL), |
139 | | fToCalendar(NULL), |
140 | | fLocale(itvfmt.fLocale), |
141 | | fDatePattern(NULL), |
142 | | fTimePattern(NULL), |
143 | 0 | fDateTimeFormat(NULL) { |
144 | 0 | *this = itvfmt; |
145 | 0 | } |
146 | | |
147 | | |
148 | | DateIntervalFormat& |
149 | 0 | DateIntervalFormat::operator=(const DateIntervalFormat& itvfmt) { |
150 | 0 | if ( this != &itvfmt ) { |
151 | 0 | delete fDateFormat; |
152 | 0 | delete fInfo; |
153 | 0 | delete fFromCalendar; |
154 | 0 | delete fToCalendar; |
155 | 0 | delete fDatePattern; |
156 | 0 | delete fTimePattern; |
157 | 0 | delete fDateTimeFormat; |
158 | 0 | { |
159 | 0 | Mutex lock(&gFormatterMutex); |
160 | 0 | if ( itvfmt.fDateFormat ) { |
161 | 0 | fDateFormat = (SimpleDateFormat*)itvfmt.fDateFormat->clone(); |
162 | 0 | } else { |
163 | 0 | fDateFormat = NULL; |
164 | 0 | } |
165 | 0 | if ( itvfmt.fFromCalendar ) { |
166 | 0 | fFromCalendar = itvfmt.fFromCalendar->clone(); |
167 | 0 | } else { |
168 | 0 | fFromCalendar = NULL; |
169 | 0 | } |
170 | 0 | if ( itvfmt.fToCalendar ) { |
171 | 0 | fToCalendar = itvfmt.fToCalendar->clone(); |
172 | 0 | } else { |
173 | 0 | fToCalendar = NULL; |
174 | 0 | } |
175 | 0 | } |
176 | 0 | if ( itvfmt.fInfo ) { |
177 | 0 | fInfo = itvfmt.fInfo->clone(); |
178 | 0 | } else { |
179 | 0 | fInfo = NULL; |
180 | 0 | } |
181 | 0 | fSkeleton = itvfmt.fSkeleton; |
182 | 0 | int8_t i; |
183 | 0 | for ( i = 0; i< DateIntervalInfo::kIPI_MAX_INDEX; ++i ) { |
184 | 0 | fIntervalPatterns[i] = itvfmt.fIntervalPatterns[i]; |
185 | 0 | } |
186 | 0 | fLocale = itvfmt.fLocale; |
187 | 0 | fDatePattern = (itvfmt.fDatePattern)? (UnicodeString*)itvfmt.fDatePattern->clone(): NULL; |
188 | 0 | fTimePattern = (itvfmt.fTimePattern)? (UnicodeString*)itvfmt.fTimePattern->clone(): NULL; |
189 | 0 | fDateTimeFormat = (itvfmt.fDateTimeFormat)? (UnicodeString*)itvfmt.fDateTimeFormat->clone(): NULL; |
190 | 0 | } |
191 | 0 | return *this; |
192 | 0 | } |
193 | | |
194 | | |
195 | 0 | DateIntervalFormat::~DateIntervalFormat() { |
196 | 0 | delete fInfo; |
197 | 0 | delete fDateFormat; |
198 | 0 | delete fFromCalendar; |
199 | 0 | delete fToCalendar; |
200 | 0 | delete fDatePattern; |
201 | 0 | delete fTimePattern; |
202 | 0 | delete fDateTimeFormat; |
203 | 0 | } |
204 | | |
205 | | |
206 | | Format* |
207 | 0 | DateIntervalFormat::clone(void) const { |
208 | 0 | return new DateIntervalFormat(*this); |
209 | 0 | } |
210 | | |
211 | | |
212 | | UBool |
213 | 0 | DateIntervalFormat::operator==(const Format& other) const { |
214 | 0 | if (typeid(*this) != typeid(other)) {return FALSE;} |
215 | 0 | const DateIntervalFormat* fmt = (DateIntervalFormat*)&other; |
216 | 0 | if (this == fmt) {return TRUE;} |
217 | 0 | if (!Format::operator==(other)) {return FALSE;} |
218 | 0 | if ((fInfo != fmt->fInfo) && (fInfo == NULL || fmt->fInfo == NULL)) {return FALSE;} |
219 | 0 | if (fInfo && fmt->fInfo && (*fInfo != *fmt->fInfo )) {return FALSE;} |
220 | 0 | { |
221 | 0 | Mutex lock(&gFormatterMutex); |
222 | 0 | if (fDateFormat != fmt->fDateFormat && (fDateFormat == NULL || fmt->fDateFormat == NULL)) {return FALSE;} |
223 | 0 | if (fDateFormat && fmt->fDateFormat && (*fDateFormat != *fmt->fDateFormat)) {return FALSE;} |
224 | 0 | } |
225 | 0 | // note: fFromCalendar and fToCalendar hold no persistent state, and therefore do not participate in operator ==. |
226 | 0 | // fDateFormat has the master calendar for the DateIntervalFormat. |
227 | 0 | if (fSkeleton != fmt->fSkeleton) {return FALSE;} |
228 | 0 | if (fDatePattern != fmt->fDatePattern && (fDatePattern == NULL || fmt->fDatePattern == NULL)) {return FALSE;} |
229 | 0 | if (fDatePattern && fmt->fDatePattern && (*fDatePattern != *fmt->fDatePattern)) {return FALSE;} |
230 | 0 | if (fTimePattern != fmt->fTimePattern && (fTimePattern == NULL || fmt->fTimePattern == NULL)) {return FALSE;} |
231 | 0 | if (fTimePattern && fmt->fTimePattern && (*fTimePattern != *fmt->fTimePattern)) {return FALSE;} |
232 | 0 | if (fDateTimeFormat != fmt->fDateTimeFormat && (fDateTimeFormat == NULL || fmt->fDateTimeFormat == NULL)) {return FALSE;} |
233 | 0 | if (fDateTimeFormat && fmt->fDateTimeFormat && (*fDateTimeFormat != *fmt->fDateTimeFormat)) {return FALSE;} |
234 | 0 | if (fLocale != fmt->fLocale) {return FALSE;} |
235 | 0 |
|
236 | 0 | for (int32_t i = 0; i< DateIntervalInfo::kIPI_MAX_INDEX; ++i ) { |
237 | 0 | if (fIntervalPatterns[i].firstPart != fmt->fIntervalPatterns[i].firstPart) {return FALSE;} |
238 | 0 | if (fIntervalPatterns[i].secondPart != fmt->fIntervalPatterns[i].secondPart ) {return FALSE;} |
239 | 0 | if (fIntervalPatterns[i].laterDateFirst != fmt->fIntervalPatterns[i].laterDateFirst) {return FALSE;} |
240 | 0 | } |
241 | 0 | return TRUE; |
242 | 0 | } |
243 | | |
244 | | |
245 | | UnicodeString& |
246 | | DateIntervalFormat::format(const Formattable& obj, |
247 | | UnicodeString& appendTo, |
248 | | FieldPosition& fieldPosition, |
249 | 0 | UErrorCode& status) const { |
250 | 0 | if ( U_FAILURE(status) ) { |
251 | 0 | return appendTo; |
252 | 0 | } |
253 | 0 | |
254 | 0 | if ( obj.getType() == Formattable::kObject ) { |
255 | 0 | const UObject* formatObj = obj.getObject(); |
256 | 0 | const DateInterval* interval = dynamic_cast<const DateInterval*>(formatObj); |
257 | 0 | if (interval != NULL) { |
258 | 0 | return format(interval, appendTo, fieldPosition, status); |
259 | 0 | } |
260 | 0 | } |
261 | 0 | status = U_ILLEGAL_ARGUMENT_ERROR; |
262 | 0 | return appendTo; |
263 | 0 | } |
264 | | |
265 | | |
266 | | UnicodeString& |
267 | | DateIntervalFormat::format(const DateInterval* dtInterval, |
268 | | UnicodeString& appendTo, |
269 | | FieldPosition& fieldPosition, |
270 | 0 | UErrorCode& status) const { |
271 | 0 | if ( U_FAILURE(status) ) { |
272 | 0 | return appendTo; |
273 | 0 | } |
274 | 0 | if (fFromCalendar == NULL || fToCalendar == NULL || fDateFormat == NULL || fInfo == NULL) { |
275 | 0 | status = U_INVALID_STATE_ERROR; |
276 | 0 | return appendTo; |
277 | 0 | } |
278 | 0 | |
279 | 0 | Mutex lock(&gFormatterMutex); |
280 | 0 | fFromCalendar->setTime(dtInterval->getFromDate(), status); |
281 | 0 | fToCalendar->setTime(dtInterval->getToDate(), status); |
282 | 0 | return formatImpl(*fFromCalendar, *fToCalendar, appendTo,fieldPosition, status); |
283 | 0 | } |
284 | | |
285 | | |
286 | | UnicodeString& |
287 | | DateIntervalFormat::format(Calendar& fromCalendar, |
288 | | Calendar& toCalendar, |
289 | | UnicodeString& appendTo, |
290 | | FieldPosition& pos, |
291 | 0 | UErrorCode& status) const { |
292 | 0 | Mutex lock(&gFormatterMutex); |
293 | 0 | return formatImpl(fromCalendar, toCalendar, appendTo, pos, status); |
294 | 0 | } |
295 | | |
296 | | |
297 | | UnicodeString& |
298 | | DateIntervalFormat::formatImpl(Calendar& fromCalendar, |
299 | | Calendar& toCalendar, |
300 | | UnicodeString& appendTo, |
301 | | FieldPosition& pos, |
302 | 0 | UErrorCode& status) const { |
303 | 0 | if ( U_FAILURE(status) ) { |
304 | 0 | return appendTo; |
305 | 0 | } |
306 | 0 | |
307 | 0 | // not support different calendar types and time zones |
308 | 0 | //if ( fromCalendar.getType() != toCalendar.getType() ) { |
309 | 0 | if ( !fromCalendar.isEquivalentTo(toCalendar) ) { |
310 | 0 | status = U_ILLEGAL_ARGUMENT_ERROR; |
311 | 0 | return appendTo; |
312 | 0 | } |
313 | 0 | |
314 | 0 | // First, find the largest different calendar field. |
315 | 0 | UCalendarDateFields field = UCAL_FIELD_COUNT; |
316 | 0 |
|
317 | 0 | if ( fromCalendar.get(UCAL_ERA,status) != toCalendar.get(UCAL_ERA,status)) { |
318 | 0 | field = UCAL_ERA; |
319 | 0 | } else if ( fromCalendar.get(UCAL_YEAR, status) != |
320 | 0 | toCalendar.get(UCAL_YEAR, status) ) { |
321 | 0 | field = UCAL_YEAR; |
322 | 0 | } else if ( fromCalendar.get(UCAL_MONTH, status) != |
323 | 0 | toCalendar.get(UCAL_MONTH, status) ) { |
324 | 0 | field = UCAL_MONTH; |
325 | 0 | } else if ( fromCalendar.get(UCAL_DATE, status) != |
326 | 0 | toCalendar.get(UCAL_DATE, status) ) { |
327 | 0 | field = UCAL_DATE; |
328 | 0 | } else if ( fromCalendar.get(UCAL_AM_PM, status) != |
329 | 0 | toCalendar.get(UCAL_AM_PM, status) ) { |
330 | 0 | field = UCAL_AM_PM; |
331 | 0 | } else if ( fromCalendar.get(UCAL_HOUR, status) != |
332 | 0 | toCalendar.get(UCAL_HOUR, status) ) { |
333 | 0 | field = UCAL_HOUR; |
334 | 0 | } else if ( fromCalendar.get(UCAL_MINUTE, status) != |
335 | 0 | toCalendar.get(UCAL_MINUTE, status) ) { |
336 | 0 | field = UCAL_MINUTE; |
337 | 0 | } else if ( fromCalendar.get(UCAL_SECOND, status) != |
338 | 0 | toCalendar.get(UCAL_SECOND, status) ) { |
339 | 0 | field = UCAL_SECOND; |
340 | 0 | } |
341 | 0 |
|
342 | 0 | if ( U_FAILURE(status) ) { |
343 | 0 | return appendTo; |
344 | 0 | } |
345 | 0 | if ( field == UCAL_FIELD_COUNT ) { |
346 | 0 | /* ignore the millisecond etc. small fields' difference. |
347 | 0 | * use single date when all the above are the same. |
348 | 0 | */ |
349 | 0 | return fDateFormat->format(fromCalendar, appendTo, pos); |
350 | 0 | } |
351 | 0 | UBool fromToOnSameDay = (field==UCAL_AM_PM || field==UCAL_HOUR || field==UCAL_MINUTE || field==UCAL_SECOND); |
352 | 0 |
|
353 | 0 | // following call should not set wrong status, |
354 | 0 | // all the pass-in fields are valid till here |
355 | 0 | int32_t itvPtnIndex = DateIntervalInfo::calendarFieldToIntervalIndex(field, |
356 | 0 | status); |
357 | 0 | const PatternInfo& intervalPattern = fIntervalPatterns[itvPtnIndex]; |
358 | 0 |
|
359 | 0 | if ( intervalPattern.firstPart.isEmpty() && |
360 | 0 | intervalPattern.secondPart.isEmpty() ) { |
361 | 0 | if ( fDateFormat->isFieldUnitIgnored(field) ) { |
362 | 0 | /* the largest different calendar field is small than |
363 | 0 | * the smallest calendar field in pattern, |
364 | 0 | * return single date format. |
365 | 0 | */ |
366 | 0 | return fDateFormat->format(fromCalendar, appendTo, pos); |
367 | 0 | } |
368 | 0 | return fallbackFormat(fromCalendar, toCalendar, fromToOnSameDay, appendTo, pos, status); |
369 | 0 | } |
370 | 0 | // If the first part in interval pattern is empty, |
371 | 0 | // the 2nd part of it saves the full-pattern used in fall-back. |
372 | 0 | // For a 'real' interval pattern, the first part will never be empty. |
373 | 0 | if ( intervalPattern.firstPart.isEmpty() ) { |
374 | 0 | // fall back |
375 | 0 | UnicodeString originalPattern; |
376 | 0 | fDateFormat->toPattern(originalPattern); |
377 | 0 | fDateFormat->applyPattern(intervalPattern.secondPart); |
378 | 0 | appendTo = fallbackFormat(fromCalendar, toCalendar, fromToOnSameDay, appendTo, pos, status); |
379 | 0 | fDateFormat->applyPattern(originalPattern); |
380 | 0 | return appendTo; |
381 | 0 | } |
382 | 0 | Calendar* firstCal; |
383 | 0 | Calendar* secondCal; |
384 | 0 | if ( intervalPattern.laterDateFirst ) { |
385 | 0 | firstCal = &toCalendar; |
386 | 0 | secondCal = &fromCalendar; |
387 | 0 | } else { |
388 | 0 | firstCal = &fromCalendar; |
389 | 0 | secondCal = &toCalendar; |
390 | 0 | } |
391 | 0 | // break the interval pattern into 2 parts, |
392 | 0 | // first part should not be empty, |
393 | 0 | UnicodeString originalPattern; |
394 | 0 | fDateFormat->toPattern(originalPattern); |
395 | 0 | fDateFormat->applyPattern(intervalPattern.firstPart); |
396 | 0 | fDateFormat->format(*firstCal, appendTo, pos); |
397 | 0 | if ( !intervalPattern.secondPart.isEmpty() ) { |
398 | 0 | fDateFormat->applyPattern(intervalPattern.secondPart); |
399 | 0 | FieldPosition otherPos; |
400 | 0 | otherPos.setField(pos.getField()); |
401 | 0 | fDateFormat->format(*secondCal, appendTo, otherPos); |
402 | 0 | if (pos.getEndIndex() == 0 && otherPos.getEndIndex() > 0) { |
403 | 0 | pos = otherPos; |
404 | 0 | } |
405 | 0 | } |
406 | 0 | fDateFormat->applyPattern(originalPattern); |
407 | 0 | return appendTo; |
408 | 0 | } |
409 | | |
410 | | |
411 | | |
412 | | void |
413 | | DateIntervalFormat::parseObject(const UnicodeString& /* source */, |
414 | | Formattable& /* result */, |
415 | 0 | ParsePosition& /* parse_pos */) const { |
416 | 0 | // parseObject(const UnicodeString&, Formattable&, UErrorCode&) const |
417 | 0 | // will set status as U_INVALID_FORMAT_ERROR if |
418 | 0 | // parse_pos is still 0 |
419 | 0 | } |
420 | | |
421 | | |
422 | | |
423 | | |
424 | | const DateIntervalInfo* |
425 | 0 | DateIntervalFormat::getDateIntervalInfo() const { |
426 | 0 | return fInfo; |
427 | 0 | } |
428 | | |
429 | | |
430 | | void |
431 | | DateIntervalFormat::setDateIntervalInfo(const DateIntervalInfo& newItvPattern, |
432 | 0 | UErrorCode& status) { |
433 | 0 | delete fInfo; |
434 | 0 | fInfo = new DateIntervalInfo(newItvPattern); |
435 | 0 |
|
436 | 0 | // Delete patterns that get reset by initializePattern |
437 | 0 | delete fDatePattern; |
438 | 0 | fDatePattern = NULL; |
439 | 0 | delete fTimePattern; |
440 | 0 | fTimePattern = NULL; |
441 | 0 | delete fDateTimeFormat; |
442 | 0 | fDateTimeFormat = NULL; |
443 | 0 |
|
444 | 0 | if (fDateFormat) { |
445 | 0 | initializePattern(status); |
446 | 0 | } |
447 | 0 | } |
448 | | |
449 | | |
450 | | |
451 | | const DateFormat* |
452 | 0 | DateIntervalFormat::getDateFormat() const { |
453 | 0 | return fDateFormat; |
454 | 0 | } |
455 | | |
456 | | |
457 | | void |
458 | | DateIntervalFormat::adoptTimeZone(TimeZone* zone) |
459 | 0 | { |
460 | 0 | if (fDateFormat != NULL) { |
461 | 0 | fDateFormat->adoptTimeZone(zone); |
462 | 0 | } |
463 | 0 | // The fDateFormat has the master calendar for the DateIntervalFormat and has |
464 | 0 | // ownership of any adopted TimeZone; fFromCalendar and fToCalendar are internal |
465 | 0 | // work clones of that calendar (and should not also be given ownership of the |
466 | 0 | // adopted TimeZone). |
467 | 0 | if (fFromCalendar) { |
468 | 0 | fFromCalendar->setTimeZone(*zone); |
469 | 0 | } |
470 | 0 | if (fToCalendar) { |
471 | 0 | fToCalendar->setTimeZone(*zone); |
472 | 0 | } |
473 | 0 | } |
474 | | |
475 | | void |
476 | | DateIntervalFormat::setTimeZone(const TimeZone& zone) |
477 | 0 | { |
478 | 0 | if (fDateFormat != NULL) { |
479 | 0 | fDateFormat->setTimeZone(zone); |
480 | 0 | } |
481 | 0 | // The fDateFormat has the master calendar for the DateIntervalFormat; |
482 | 0 | // fFromCalendar and fToCalendar are internal work clones of that calendar. |
483 | 0 | if (fFromCalendar) { |
484 | 0 | fFromCalendar->setTimeZone(zone); |
485 | 0 | } |
486 | 0 | if (fToCalendar) { |
487 | 0 | fToCalendar->setTimeZone(zone); |
488 | 0 | } |
489 | 0 | } |
490 | | |
491 | | const TimeZone& |
492 | | DateIntervalFormat::getTimeZone() const |
493 | 0 | { |
494 | 0 | if (fDateFormat != NULL) { |
495 | 0 | Mutex lock(&gFormatterMutex); |
496 | 0 | return fDateFormat->getTimeZone(); |
497 | 0 | } |
498 | 0 | // If fDateFormat is NULL (unexpected), create default timezone. |
499 | 0 | return *(TimeZone::createDefault()); |
500 | 0 | } |
501 | | |
502 | | DateIntervalFormat::DateIntervalFormat(const Locale& locale, |
503 | | DateIntervalInfo* dtItvInfo, |
504 | | const UnicodeString* skeleton, |
505 | | UErrorCode& status) |
506 | | : fInfo(NULL), |
507 | | fDateFormat(NULL), |
508 | | fFromCalendar(NULL), |
509 | | fToCalendar(NULL), |
510 | | fLocale(locale), |
511 | | fDatePattern(NULL), |
512 | | fTimePattern(NULL), |
513 | | fDateTimeFormat(NULL) |
514 | 0 | { |
515 | 0 | LocalPointer<DateIntervalInfo> info(dtItvInfo, status); |
516 | 0 | LocalPointer<SimpleDateFormat> dtfmt(static_cast<SimpleDateFormat *>( |
517 | 0 | DateFormat::createInstanceForSkeleton(*skeleton, locale, status)), status); |
518 | 0 | if (U_FAILURE(status)) { |
519 | 0 | return; |
520 | 0 | } |
521 | 0 | |
522 | 0 | if ( skeleton ) { |
523 | 0 | fSkeleton = *skeleton; |
524 | 0 | } |
525 | 0 | fInfo = info.orphan(); |
526 | 0 | fDateFormat = dtfmt.orphan(); |
527 | 0 | if ( fDateFormat->getCalendar() ) { |
528 | 0 | fFromCalendar = fDateFormat->getCalendar()->clone(); |
529 | 0 | fToCalendar = fDateFormat->getCalendar()->clone(); |
530 | 0 | } |
531 | 0 | initializePattern(status); |
532 | 0 | } |
533 | | |
534 | | DateIntervalFormat* U_EXPORT2 |
535 | | DateIntervalFormat::create(const Locale& locale, |
536 | | DateIntervalInfo* dtitvinf, |
537 | | const UnicodeString* skeleton, |
538 | 0 | UErrorCode& status) { |
539 | 0 | DateIntervalFormat* f = new DateIntervalFormat(locale, dtitvinf, |
540 | 0 | skeleton, status); |
541 | 0 | if ( f == NULL ) { |
542 | 0 | status = U_MEMORY_ALLOCATION_ERROR; |
543 | 0 | delete dtitvinf; |
544 | 0 | } else if ( U_FAILURE(status) ) { |
545 | 0 | // safe to delete f, although nothing acutally is saved |
546 | 0 | delete f; |
547 | 0 | f = 0; |
548 | 0 | } |
549 | 0 | return f; |
550 | 0 | } |
551 | | |
552 | | |
553 | | |
554 | | /** |
555 | | * Initialize interval patterns locale to this formatter |
556 | | * |
557 | | * This code is a bit complicated since |
558 | | * 1. the interval patterns saved in resource bundle files are interval |
559 | | * patterns based on date or time only. |
560 | | * It does not have interval patterns based on both date and time. |
561 | | * Interval patterns on both date and time are algorithm generated. |
562 | | * |
563 | | * For example, it has interval patterns on skeleton "dMy" and "hm", |
564 | | * but it does not have interval patterns on skeleton "dMyhm". |
565 | | * |
566 | | * The rule to genearte interval patterns for both date and time skeleton are |
567 | | * 1) when the year, month, or day differs, concatenate the two original |
568 | | * expressions with a separator between, |
569 | | * For example, interval pattern from "Jan 10, 2007 10:10 am" |
570 | | * to "Jan 11, 2007 10:10am" is |
571 | | * "Jan 10, 2007 10:10 am - Jan 11, 2007 10:10am" |
572 | | * |
573 | | * 2) otherwise, present the date followed by the range expression |
574 | | * for the time. |
575 | | * For example, interval pattern from "Jan 10, 2007 10:10 am" |
576 | | * to "Jan 10, 2007 11:10am" is |
577 | | * "Jan 10, 2007 10:10 am - 11:10am" |
578 | | * |
579 | | * 2. even a pattern does not request a certion calendar field, |
580 | | * the interval pattern needs to include such field if such fields are |
581 | | * different between 2 dates. |
582 | | * For example, a pattern/skeleton is "hm", but the interval pattern |
583 | | * includes year, month, and date when year, month, and date differs. |
584 | | * |
585 | | * @param status output param set to success/failure code on exit |
586 | | * @stable ICU 4.0 |
587 | | */ |
588 | | void |
589 | 0 | DateIntervalFormat::initializePattern(UErrorCode& status) { |
590 | 0 | if ( U_FAILURE(status) ) { |
591 | 0 | return; |
592 | 0 | } |
593 | 0 | const Locale& locale = fDateFormat->getSmpFmtLocale(); |
594 | 0 | if ( fSkeleton.isEmpty() ) { |
595 | 0 | UnicodeString fullPattern; |
596 | 0 | fDateFormat->toPattern(fullPattern); |
597 | | #ifdef DTITVFMT_DEBUG |
598 | | char result[1000]; |
599 | | char result_1[1000]; |
600 | | char mesg[2000]; |
601 | | fSkeleton.extract(0, fSkeleton.length(), result, "UTF-8"); |
602 | | sprintf(mesg, "in getBestSkeleton: fSkeleton: %s; \n", result); |
603 | | PRINTMESG(mesg) |
604 | | #endif |
605 | | // fSkeleton is already set by createDateIntervalInstance() |
606 | 0 | // or by createInstance(UnicodeString skeleton, .... ) |
607 | 0 | fSkeleton = DateTimePatternGenerator::staticGetSkeleton( |
608 | 0 | fullPattern, status); |
609 | 0 | if ( U_FAILURE(status) ) { |
610 | 0 | return; |
611 | 0 | } |
612 | 0 | } |
613 | 0 | |
614 | 0 | // initialize the fIntervalPattern ordering |
615 | 0 | int8_t i; |
616 | 0 | for ( i = 0; i < DateIntervalInfo::kIPI_MAX_INDEX; ++i ) { |
617 | 0 | fIntervalPatterns[i].laterDateFirst = fInfo->getDefaultOrder(); |
618 | 0 | } |
619 | 0 |
|
620 | 0 | /* Check whether the skeleton is a combination of date and time. |
621 | 0 | * For the complication reason 1 explained above. |
622 | 0 | */ |
623 | 0 | UnicodeString dateSkeleton; |
624 | 0 | UnicodeString timeSkeleton; |
625 | 0 | UnicodeString normalizedTimeSkeleton; |
626 | 0 | UnicodeString normalizedDateSkeleton; |
627 | 0 |
|
628 | 0 |
|
629 | 0 | /* the difference between time skeleton and normalizedTimeSkeleton are: |
630 | 0 | * 1. (Formerly, normalized time skeleton folded 'H' to 'h'; no longer true) |
631 | 0 | * 2. 'a' is omitted in normalized time skeleton. |
632 | 0 | * 3. there is only one appearance for 'h' or 'H', 'm','v', 'z' in normalized |
633 | 0 | * time skeleton |
634 | 0 | * |
635 | 0 | * The difference between date skeleton and normalizedDateSkeleton are: |
636 | 0 | * 1. both 'y' and 'd' appear only once in normalizeDateSkeleton |
637 | 0 | * 2. 'E' and 'EE' are normalized into 'EEE' |
638 | 0 | * 3. 'MM' is normalized into 'M' |
639 | 0 | */ |
640 | 0 | getDateTimeSkeleton(fSkeleton, dateSkeleton, normalizedDateSkeleton, |
641 | 0 | timeSkeleton, normalizedTimeSkeleton); |
642 | 0 |
|
643 | | #ifdef DTITVFMT_DEBUG |
644 | | char result[1000]; |
645 | | char result_1[1000]; |
646 | | char mesg[2000]; |
647 | | fSkeleton.extract(0, fSkeleton.length(), result, "UTF-8"); |
648 | | sprintf(mesg, "in getBestSkeleton: fSkeleton: %s; \n", result); |
649 | | PRINTMESG(mesg) |
650 | | #endif |
651 | |
|
652 | 0 | // move this up here since we need it for fallbacks |
653 | 0 | if ( timeSkeleton.length() > 0 && dateSkeleton.length() > 0 ) { |
654 | 0 | // Need the Date/Time pattern for concatenation of the date |
655 | 0 | // with the time interval. |
656 | 0 | // The date/time pattern ( such as {0} {1} ) is saved in |
657 | 0 | // calendar, that is why need to get the CalendarData here. |
658 | 0 | LocalUResourceBundlePointer dateTimePatternsRes(ures_open(NULL, locale.getBaseName(), &status)); |
659 | 0 | ures_getByKey(dateTimePatternsRes.getAlias(), gCalendarTag, |
660 | 0 | dateTimePatternsRes.getAlias(), &status); |
661 | 0 | ures_getByKeyWithFallback(dateTimePatternsRes.getAlias(), gGregorianTag, |
662 | 0 | dateTimePatternsRes.getAlias(), &status); |
663 | 0 | ures_getByKeyWithFallback(dateTimePatternsRes.getAlias(), gDateTimePatternsTag, |
664 | 0 | dateTimePatternsRes.getAlias(), &status); |
665 | 0 |
|
666 | 0 | int32_t dateTimeFormatLength; |
667 | 0 | const UChar* dateTimeFormat = ures_getStringByIndex( |
668 | 0 | dateTimePatternsRes.getAlias(), |
669 | 0 | (int32_t)DateFormat::kDateTime, |
670 | 0 | &dateTimeFormatLength, &status); |
671 | 0 | if ( U_SUCCESS(status) && dateTimeFormatLength >= 3 ) { |
672 | 0 | fDateTimeFormat = new UnicodeString(dateTimeFormat, dateTimeFormatLength); |
673 | 0 | } |
674 | 0 | } |
675 | 0 |
|
676 | 0 | UBool found = setSeparateDateTimePtn(normalizedDateSkeleton, |
677 | 0 | normalizedTimeSkeleton); |
678 | 0 |
|
679 | 0 | // for skeletons with seconds, found is false and we enter this block |
680 | 0 | if ( found == false ) { |
681 | 0 | // use fallback |
682 | 0 | // TODO: if user asks "m"(minute), but "d"(day) differ |
683 | 0 | if ( timeSkeleton.length() != 0 ) { |
684 | 0 | if ( dateSkeleton.length() == 0 ) { |
685 | 0 | // prefix with yMd |
686 | 0 | timeSkeleton.insert(0, gDateFormatSkeleton[DateFormat::kShort], -1); |
687 | 0 | UnicodeString pattern = DateFormat::getBestPattern( |
688 | 0 | locale, timeSkeleton, status); |
689 | 0 | if ( U_FAILURE(status) ) { |
690 | 0 | return; |
691 | 0 | } |
692 | 0 | // for fall back interval patterns, |
693 | 0 | // the first part of the pattern is empty, |
694 | 0 | // the second part of the pattern is the full-pattern |
695 | 0 | // should be used in fall-back. |
696 | 0 | setPatternInfo(UCAL_DATE, NULL, &pattern, fInfo->getDefaultOrder()); |
697 | 0 | setPatternInfo(UCAL_MONTH, NULL, &pattern, fInfo->getDefaultOrder()); |
698 | 0 | setPatternInfo(UCAL_YEAR, NULL, &pattern, fInfo->getDefaultOrder()); |
699 | 0 | } else { |
700 | 0 | // TODO: fall back |
701 | 0 | } |
702 | 0 | } else { |
703 | 0 | // TODO: fall back |
704 | 0 | } |
705 | 0 | return; |
706 | 0 | } // end of skeleton not found |
707 | 0 | // interval patterns for skeleton are found in resource |
708 | 0 | if ( timeSkeleton.length() == 0 ) { |
709 | 0 | // done |
710 | 0 | } else if ( dateSkeleton.length() == 0 ) { |
711 | 0 | // prefix with yMd |
712 | 0 | timeSkeleton.insert(0, gDateFormatSkeleton[DateFormat::kShort], -1); |
713 | 0 | UnicodeString pattern = DateFormat::getBestPattern( |
714 | 0 | locale, timeSkeleton, status); |
715 | 0 | if ( U_FAILURE(status) ) { |
716 | 0 | return; |
717 | 0 | } |
718 | 0 | // for fall back interval patterns, |
719 | 0 | // the first part of the pattern is empty, |
720 | 0 | // the second part of the pattern is the full-pattern |
721 | 0 | // should be used in fall-back. |
722 | 0 | setPatternInfo(UCAL_DATE, NULL, &pattern, fInfo->getDefaultOrder()); |
723 | 0 | setPatternInfo(UCAL_MONTH, NULL, &pattern, fInfo->getDefaultOrder()); |
724 | 0 | setPatternInfo(UCAL_YEAR, NULL, &pattern, fInfo->getDefaultOrder()); |
725 | 0 | } else { |
726 | 0 | /* if both present, |
727 | 0 | * 1) when the year, month, or day differs, |
728 | 0 | * concatenate the two original expressions with a separator between, |
729 | 0 | * 2) otherwise, present the date followed by the |
730 | 0 | * range expression for the time. |
731 | 0 | */ |
732 | 0 | /* |
733 | 0 | * 1) when the year, month, or day differs, |
734 | 0 | * concatenate the two original expressions with a separator between, |
735 | 0 | */ |
736 | 0 | // if field exists, use fall back |
737 | 0 | UnicodeString skeleton = fSkeleton; |
738 | 0 | if ( !fieldExistsInSkeleton(UCAL_DATE, dateSkeleton) ) { |
739 | 0 | // prefix skeleton with 'd' |
740 | 0 | skeleton.insert(0, LOW_D); |
741 | 0 | setFallbackPattern(UCAL_DATE, skeleton, status); |
742 | 0 | } |
743 | 0 | if ( !fieldExistsInSkeleton(UCAL_MONTH, dateSkeleton) ) { |
744 | 0 | // then prefix skeleton with 'M' |
745 | 0 | skeleton.insert(0, CAP_M); |
746 | 0 | setFallbackPattern(UCAL_MONTH, skeleton, status); |
747 | 0 | } |
748 | 0 | if ( !fieldExistsInSkeleton(UCAL_YEAR, dateSkeleton) ) { |
749 | 0 | // then prefix skeleton with 'y' |
750 | 0 | skeleton.insert(0, LOW_Y); |
751 | 0 | setFallbackPattern(UCAL_YEAR, skeleton, status); |
752 | 0 | } |
753 | 0 |
|
754 | 0 | /* |
755 | 0 | * 2) otherwise, present the date followed by the |
756 | 0 | * range expression for the time. |
757 | 0 | */ |
758 | 0 |
|
759 | 0 | if ( fDateTimeFormat == NULL ) { |
760 | 0 | // earlier failure getting dateTimeFormat |
761 | 0 | return; |
762 | 0 | } |
763 | 0 | |
764 | 0 | UnicodeString datePattern = DateFormat::getBestPattern( |
765 | 0 | locale, dateSkeleton, status); |
766 | 0 |
|
767 | 0 | concatSingleDate2TimeInterval(*fDateTimeFormat, datePattern, UCAL_AM_PM, status); |
768 | 0 | concatSingleDate2TimeInterval(*fDateTimeFormat, datePattern, UCAL_HOUR, status); |
769 | 0 | concatSingleDate2TimeInterval(*fDateTimeFormat, datePattern, UCAL_MINUTE, status); |
770 | 0 | } |
771 | 0 | } |
772 | | |
773 | | |
774 | | |
775 | | void U_EXPORT2 |
776 | | DateIntervalFormat::getDateTimeSkeleton(const UnicodeString& skeleton, |
777 | | UnicodeString& dateSkeleton, |
778 | | UnicodeString& normalizedDateSkeleton, |
779 | | UnicodeString& timeSkeleton, |
780 | 0 | UnicodeString& normalizedTimeSkeleton) { |
781 | 0 | // dateSkeleton follows the sequence of y*M*E*d* |
782 | 0 | // timeSkeleton follows the sequence of hm*[v|z]? |
783 | 0 | int32_t ECount = 0; |
784 | 0 | int32_t dCount = 0; |
785 | 0 | int32_t MCount = 0; |
786 | 0 | int32_t yCount = 0; |
787 | 0 | int32_t hCount = 0; |
788 | 0 | int32_t HCount = 0; |
789 | 0 | int32_t mCount = 0; |
790 | 0 | int32_t vCount = 0; |
791 | 0 | int32_t zCount = 0; |
792 | 0 | int32_t i; |
793 | 0 |
|
794 | 0 | for (i = 0; i < skeleton.length(); ++i) { |
795 | 0 | UChar ch = skeleton[i]; |
796 | 0 | switch ( ch ) { |
797 | 0 | case CAP_E: |
798 | 0 | dateSkeleton.append(ch); |
799 | 0 | ++ECount; |
800 | 0 | break; |
801 | 0 | case LOW_D: |
802 | 0 | dateSkeleton.append(ch); |
803 | 0 | ++dCount; |
804 | 0 | break; |
805 | 0 | case CAP_M: |
806 | 0 | dateSkeleton.append(ch); |
807 | 0 | ++MCount; |
808 | 0 | break; |
809 | 0 | case LOW_Y: |
810 | 0 | dateSkeleton.append(ch); |
811 | 0 | ++yCount; |
812 | 0 | break; |
813 | 0 | case CAP_G: |
814 | 0 | case CAP_Y: |
815 | 0 | case LOW_U: |
816 | 0 | case CAP_Q: |
817 | 0 | case LOW_Q: |
818 | 0 | case CAP_L: |
819 | 0 | case LOW_L: |
820 | 0 | case CAP_W: |
821 | 0 | case LOW_W: |
822 | 0 | case CAP_D: |
823 | 0 | case CAP_F: |
824 | 0 | case LOW_G: |
825 | 0 | case LOW_E: |
826 | 0 | case LOW_C: |
827 | 0 | case CAP_U: |
828 | 0 | case LOW_R: |
829 | 0 | normalizedDateSkeleton.append(ch); |
830 | 0 | dateSkeleton.append(ch); |
831 | 0 | break; |
832 | 0 | case LOW_A: |
833 | 0 | // 'a' is implicitly handled |
834 | 0 | timeSkeleton.append(ch); |
835 | 0 | break; |
836 | 0 | case LOW_H: |
837 | 0 | timeSkeleton.append(ch); |
838 | 0 | ++hCount; |
839 | 0 | break; |
840 | 0 | case CAP_H: |
841 | 0 | timeSkeleton.append(ch); |
842 | 0 | ++HCount; |
843 | 0 | break; |
844 | 0 | case LOW_M: |
845 | 0 | timeSkeleton.append(ch); |
846 | 0 | ++mCount; |
847 | 0 | break; |
848 | 0 | case LOW_Z: |
849 | 0 | ++zCount; |
850 | 0 | timeSkeleton.append(ch); |
851 | 0 | break; |
852 | 0 | case LOW_V: |
853 | 0 | ++vCount; |
854 | 0 | timeSkeleton.append(ch); |
855 | 0 | break; |
856 | 0 | case CAP_V: |
857 | 0 | case CAP_Z: |
858 | 0 | case LOW_K: |
859 | 0 | case CAP_K: |
860 | 0 | case LOW_J: |
861 | 0 | case LOW_S: |
862 | 0 | case CAP_S: |
863 | 0 | case CAP_A: |
864 | 0 | timeSkeleton.append(ch); |
865 | 0 | normalizedTimeSkeleton.append(ch); |
866 | 0 | break; |
867 | 0 | } |
868 | 0 | } |
869 | 0 |
|
870 | 0 | /* generate normalized form for date*/ |
871 | 0 | if ( yCount != 0 ) { |
872 | 0 | for (i = 0; i < yCount; ++i) { |
873 | 0 | normalizedDateSkeleton.append(LOW_Y); |
874 | 0 | } |
875 | 0 | } |
876 | 0 | if ( MCount != 0 ) { |
877 | 0 | if ( MCount < 3 ) { |
878 | 0 | normalizedDateSkeleton.append(CAP_M); |
879 | 0 | } else { |
880 | 0 | int32_t i; |
881 | 0 | for ( i = 0; i < MCount && i < MAX_M_COUNT; ++i ) { |
882 | 0 | normalizedDateSkeleton.append(CAP_M); |
883 | 0 | } |
884 | 0 | } |
885 | 0 | } |
886 | 0 | if ( ECount != 0 ) { |
887 | 0 | if ( ECount <= 3 ) { |
888 | 0 | normalizedDateSkeleton.append(CAP_E); |
889 | 0 | } else { |
890 | 0 | int32_t i; |
891 | 0 | for ( i = 0; i < ECount && i < MAX_E_COUNT; ++i ) { |
892 | 0 | normalizedDateSkeleton.append(CAP_E); |
893 | 0 | } |
894 | 0 | } |
895 | 0 | } |
896 | 0 | if ( dCount != 0 ) { |
897 | 0 | normalizedDateSkeleton.append(LOW_D); |
898 | 0 | } |
899 | 0 |
|
900 | 0 | /* generate normalized form for time */ |
901 | 0 | if ( HCount != 0 ) { |
902 | 0 | normalizedTimeSkeleton.append(CAP_H); |
903 | 0 | } |
904 | 0 | else if ( hCount != 0 ) { |
905 | 0 | normalizedTimeSkeleton.append(LOW_H); |
906 | 0 | } |
907 | 0 | if ( mCount != 0 ) { |
908 | 0 | normalizedTimeSkeleton.append(LOW_M); |
909 | 0 | } |
910 | 0 | if ( zCount != 0 ) { |
911 | 0 | normalizedTimeSkeleton.append(LOW_Z); |
912 | 0 | } |
913 | 0 | if ( vCount != 0 ) { |
914 | 0 | normalizedTimeSkeleton.append(LOW_V); |
915 | 0 | } |
916 | 0 | } |
917 | | |
918 | | |
919 | | /** |
920 | | * Generate date or time interval pattern from resource, |
921 | | * and set them into the interval pattern locale to this formatter. |
922 | | * |
923 | | * It needs to handle the following: |
924 | | * 1. need to adjust field width. |
925 | | * For example, the interval patterns saved in DateIntervalInfo |
926 | | * includes "dMMMy", but not "dMMMMy". |
927 | | * Need to get interval patterns for dMMMMy from dMMMy. |
928 | | * Another example, the interval patterns saved in DateIntervalInfo |
929 | | * includes "hmv", but not "hmz". |
930 | | * Need to get interval patterns for "hmz' from 'hmv' |
931 | | * |
932 | | * 2. there might be no pattern for 'y' differ for skeleton "Md", |
933 | | * in order to get interval patterns for 'y' differ, |
934 | | * need to look for it from skeleton 'yMd' |
935 | | * |
936 | | * @param dateSkeleton normalized date skeleton |
937 | | * @param timeSkeleton normalized time skeleton |
938 | | * @return whether the resource is found for the skeleton. |
939 | | * TRUE if interval pattern found for the skeleton, |
940 | | * FALSE otherwise. |
941 | | * @stable ICU 4.0 |
942 | | */ |
943 | | UBool |
944 | | DateIntervalFormat::setSeparateDateTimePtn( |
945 | | const UnicodeString& dateSkeleton, |
946 | 0 | const UnicodeString& timeSkeleton) { |
947 | 0 | const UnicodeString* skeleton; |
948 | 0 | // if both date and time skeleton present, |
949 | 0 | // the final interval pattern might include time interval patterns |
950 | 0 | // ( when, am_pm, hour, minute differ ), |
951 | 0 | // but not date interval patterns ( when year, month, day differ ). |
952 | 0 | // For year/month/day differ, it falls back to fall-back pattern. |
953 | 0 | if ( timeSkeleton.length() != 0 ) { |
954 | 0 | skeleton = &timeSkeleton; |
955 | 0 | } else { |
956 | 0 | skeleton = &dateSkeleton; |
957 | 0 | } |
958 | 0 |
|
959 | 0 | /* interval patterns for skeleton "dMMMy" (but not "dMMMMy") |
960 | 0 | * are defined in resource, |
961 | 0 | * interval patterns for skeleton "dMMMMy" are calculated by |
962 | 0 | * 1. get the best match skeleton for "dMMMMy", which is "dMMMy" |
963 | 0 | * 2. get the interval patterns for "dMMMy", |
964 | 0 | * 3. extend "MMM" to "MMMM" in above interval patterns for "dMMMMy" |
965 | 0 | * getBestSkeleton() is step 1. |
966 | 0 | */ |
967 | 0 | // best skeleton, and the difference information |
968 | 0 | int8_t differenceInfo = 0; |
969 | 0 | const UnicodeString* bestSkeleton = fInfo->getBestSkeleton(*skeleton, |
970 | 0 | differenceInfo); |
971 | 0 | /* best skeleton could be NULL. |
972 | 0 | For example: in "ca" resource file, |
973 | 0 | interval format is defined as following |
974 | 0 | intervalFormats{ |
975 | 0 | fallback{"{0} - {1}"} |
976 | 0 | } |
977 | 0 | there is no skeletons/interval patterns defined, |
978 | 0 | and the best skeleton match could be NULL |
979 | 0 | */ |
980 | 0 | if ( bestSkeleton == NULL ) { |
981 | 0 | return false; |
982 | 0 | } |
983 | 0 | |
984 | 0 | // Set patterns for fallback use, need to do this |
985 | 0 | // before returning if differenceInfo == -1 |
986 | 0 | UErrorCode status; |
987 | 0 | if ( dateSkeleton.length() != 0) { |
988 | 0 | status = U_ZERO_ERROR; |
989 | 0 | fDatePattern = new UnicodeString(DateFormat::getBestPattern( |
990 | 0 | fLocale, dateSkeleton, status)); |
991 | 0 | } |
992 | 0 | if ( timeSkeleton.length() != 0) { |
993 | 0 | status = U_ZERO_ERROR; |
994 | 0 | fTimePattern = new UnicodeString(DateFormat::getBestPattern( |
995 | 0 | fLocale, timeSkeleton, status)); |
996 | 0 | } |
997 | 0 |
|
998 | 0 | // difference: |
999 | 0 | // 0 means the best matched skeleton is the same as input skeleton |
1000 | 0 | // 1 means the fields are the same, but field width are different |
1001 | 0 | // 2 means the only difference between fields are v/z, |
1002 | 0 | // -1 means there are other fields difference |
1003 | 0 | // (this will happen, for instance, if the supplied skeleton has seconds, |
1004 | 0 | // but no skeletons in the intervalFormats data do) |
1005 | 0 | if ( differenceInfo == -1 ) { |
1006 | 0 | // skeleton has different fields, not only v/z difference |
1007 | 0 | return false; |
1008 | 0 | } |
1009 | 0 | |
1010 | 0 | if ( timeSkeleton.length() == 0 ) { |
1011 | 0 | UnicodeString extendedSkeleton; |
1012 | 0 | UnicodeString extendedBestSkeleton; |
1013 | 0 | // only has date skeleton |
1014 | 0 | setIntervalPattern(UCAL_DATE, skeleton, bestSkeleton, differenceInfo, |
1015 | 0 | &extendedSkeleton, &extendedBestSkeleton); |
1016 | 0 |
|
1017 | 0 | UBool extended = setIntervalPattern(UCAL_MONTH, skeleton, bestSkeleton, |
1018 | 0 | differenceInfo, |
1019 | 0 | &extendedSkeleton, &extendedBestSkeleton); |
1020 | 0 |
|
1021 | 0 | if ( extended ) { |
1022 | 0 | bestSkeleton = &extendedBestSkeleton; |
1023 | 0 | skeleton = &extendedSkeleton; |
1024 | 0 | } |
1025 | 0 | setIntervalPattern(UCAL_YEAR, skeleton, bestSkeleton, differenceInfo, |
1026 | 0 | &extendedSkeleton, &extendedBestSkeleton); |
1027 | 0 | } else { |
1028 | 0 | setIntervalPattern(UCAL_MINUTE, skeleton, bestSkeleton, differenceInfo); |
1029 | 0 | setIntervalPattern(UCAL_HOUR, skeleton, bestSkeleton, differenceInfo); |
1030 | 0 | setIntervalPattern(UCAL_AM_PM, skeleton, bestSkeleton, differenceInfo); |
1031 | 0 | } |
1032 | 0 | return true; |
1033 | 0 | } |
1034 | | |
1035 | | |
1036 | | |
1037 | | void |
1038 | | DateIntervalFormat::setFallbackPattern(UCalendarDateFields field, |
1039 | | const UnicodeString& skeleton, |
1040 | 0 | UErrorCode& status) { |
1041 | 0 | if ( U_FAILURE(status) ) { |
1042 | 0 | return; |
1043 | 0 | } |
1044 | 0 | UnicodeString pattern = DateFormat::getBestPattern( |
1045 | 0 | fLocale, skeleton, status); |
1046 | 0 | if ( U_FAILURE(status) ) { |
1047 | 0 | return; |
1048 | 0 | } |
1049 | 0 | setPatternInfo(field, NULL, &pattern, fInfo->getDefaultOrder()); |
1050 | 0 | } |
1051 | | |
1052 | | |
1053 | | |
1054 | | |
1055 | | void |
1056 | | DateIntervalFormat::setPatternInfo(UCalendarDateFields field, |
1057 | | const UnicodeString* firstPart, |
1058 | | const UnicodeString* secondPart, |
1059 | 0 | UBool laterDateFirst) { |
1060 | 0 | // for fall back interval patterns, |
1061 | 0 | // the first part of the pattern is empty, |
1062 | 0 | // the second part of the pattern is the full-pattern |
1063 | 0 | // should be used in fall-back. |
1064 | 0 | UErrorCode status = U_ZERO_ERROR; |
1065 | 0 | // following should not set any wrong status. |
1066 | 0 | int32_t itvPtnIndex = DateIntervalInfo::calendarFieldToIntervalIndex(field, |
1067 | 0 | status); |
1068 | 0 | if ( U_FAILURE(status) ) { |
1069 | 0 | return; |
1070 | 0 | } |
1071 | 0 | PatternInfo& ptn = fIntervalPatterns[itvPtnIndex]; |
1072 | 0 | if ( firstPart ) { |
1073 | 0 | ptn.firstPart = *firstPart; |
1074 | 0 | } |
1075 | 0 | if ( secondPart ) { |
1076 | 0 | ptn.secondPart = *secondPart; |
1077 | 0 | } |
1078 | 0 | ptn.laterDateFirst = laterDateFirst; |
1079 | 0 | } |
1080 | | |
1081 | | void |
1082 | | DateIntervalFormat::setIntervalPattern(UCalendarDateFields field, |
1083 | 0 | const UnicodeString& intervalPattern) { |
1084 | 0 | UBool order = fInfo->getDefaultOrder(); |
1085 | 0 | setIntervalPattern(field, intervalPattern, order); |
1086 | 0 | } |
1087 | | |
1088 | | |
1089 | | void |
1090 | | DateIntervalFormat::setIntervalPattern(UCalendarDateFields field, |
1091 | | const UnicodeString& intervalPattern, |
1092 | 0 | UBool laterDateFirst) { |
1093 | 0 | const UnicodeString* pattern = &intervalPattern; |
1094 | 0 | UBool order = laterDateFirst; |
1095 | 0 | // check for "latestFirst:" or "earliestFirst:" prefix |
1096 | 0 | int8_t prefixLength = UPRV_LENGTHOF(gLaterFirstPrefix); |
1097 | 0 | int8_t earliestFirstLength = UPRV_LENGTHOF(gEarlierFirstPrefix); |
1098 | 0 | UnicodeString realPattern; |
1099 | 0 | if ( intervalPattern.startsWith(gLaterFirstPrefix, prefixLength) ) { |
1100 | 0 | order = true; |
1101 | 0 | intervalPattern.extract(prefixLength, |
1102 | 0 | intervalPattern.length() - prefixLength, |
1103 | 0 | realPattern); |
1104 | 0 | pattern = &realPattern; |
1105 | 0 | } else if ( intervalPattern.startsWith(gEarlierFirstPrefix, |
1106 | 0 | earliestFirstLength) ) { |
1107 | 0 | order = false; |
1108 | 0 | intervalPattern.extract(earliestFirstLength, |
1109 | 0 | intervalPattern.length() - earliestFirstLength, |
1110 | 0 | realPattern); |
1111 | 0 | pattern = &realPattern; |
1112 | 0 | } |
1113 | 0 |
|
1114 | 0 | int32_t splitPoint = splitPatternInto2Part(*pattern); |
1115 | 0 |
|
1116 | 0 | UnicodeString firstPart; |
1117 | 0 | UnicodeString secondPart; |
1118 | 0 | pattern->extract(0, splitPoint, firstPart); |
1119 | 0 | if ( splitPoint < pattern->length() ) { |
1120 | 0 | pattern->extract(splitPoint, pattern->length()-splitPoint, secondPart); |
1121 | 0 | } |
1122 | 0 | setPatternInfo(field, &firstPart, &secondPart, order); |
1123 | 0 | } |
1124 | | |
1125 | | |
1126 | | |
1127 | | |
1128 | | /** |
1129 | | * Generate interval pattern from existing resource |
1130 | | * |
1131 | | * It not only save the interval patterns, |
1132 | | * but also return the extended skeleton and its best match skeleton. |
1133 | | * |
1134 | | * @param field largest different calendar field |
1135 | | * @param skeleton skeleton |
1136 | | * @param bestSkeleton the best match skeleton which has interval pattern |
1137 | | * defined in resource |
1138 | | * @param differenceInfo the difference between skeleton and best skeleton |
1139 | | * 0 means the best matched skeleton is the same as input skeleton |
1140 | | * 1 means the fields are the same, but field width are different |
1141 | | * 2 means the only difference between fields are v/z, |
1142 | | * -1 means there are other fields difference |
1143 | | * |
1144 | | * @param extendedSkeleton extended skeleton |
1145 | | * @param extendedBestSkeleton extended best match skeleton |
1146 | | * @return whether the interval pattern is found |
1147 | | * through extending skeleton or not. |
1148 | | * TRUE if interval pattern is found by |
1149 | | * extending skeleton, FALSE otherwise. |
1150 | | * @stable ICU 4.0 |
1151 | | */ |
1152 | | UBool |
1153 | | DateIntervalFormat::setIntervalPattern(UCalendarDateFields field, |
1154 | | const UnicodeString* skeleton, |
1155 | | const UnicodeString* bestSkeleton, |
1156 | | int8_t differenceInfo, |
1157 | | UnicodeString* extendedSkeleton, |
1158 | 0 | UnicodeString* extendedBestSkeleton) { |
1159 | 0 | UErrorCode status = U_ZERO_ERROR; |
1160 | 0 | // following getIntervalPattern() should not generate error status |
1161 | 0 | UnicodeString pattern; |
1162 | 0 | fInfo->getIntervalPattern(*bestSkeleton, field, pattern, status); |
1163 | 0 | if ( pattern.isEmpty() ) { |
1164 | 0 | // single date |
1165 | 0 | if ( SimpleDateFormat::isFieldUnitIgnored(*bestSkeleton, field) ) { |
1166 | 0 | // do nothing, format will handle it |
1167 | 0 | return false; |
1168 | 0 | } |
1169 | 0 | |
1170 | 0 | // for 24 hour system, interval patterns in resource file |
1171 | 0 | // might not include pattern when am_pm differ, |
1172 | 0 | // which should be the same as hour differ. |
1173 | 0 | // add it here for simplicity |
1174 | 0 | if ( field == UCAL_AM_PM ) { |
1175 | 0 | fInfo->getIntervalPattern(*bestSkeleton, UCAL_HOUR, pattern,status); |
1176 | 0 | if ( !pattern.isEmpty() ) { |
1177 | 0 | setIntervalPattern(field, pattern); |
1178 | 0 | } |
1179 | 0 | return false; |
1180 | 0 | } |
1181 | 0 | // else, looking for pattern when 'y' differ for 'dMMMM' skeleton, |
1182 | 0 | // first, get best match pattern "MMMd", |
1183 | 0 | // since there is no pattern for 'y' differs for skeleton 'MMMd', |
1184 | 0 | // need to look for it from skeleton 'yMMMd', |
1185 | 0 | // if found, adjust field width in interval pattern from |
1186 | 0 | // "MMM" to "MMMM". |
1187 | 0 | UChar fieldLetter = fgCalendarFieldToPatternLetter[field]; |
1188 | 0 | if ( extendedSkeleton ) { |
1189 | 0 | *extendedSkeleton = *skeleton; |
1190 | 0 | *extendedBestSkeleton = *bestSkeleton; |
1191 | 0 | extendedSkeleton->insert(0, fieldLetter); |
1192 | 0 | extendedBestSkeleton->insert(0, fieldLetter); |
1193 | 0 | // for example, looking for patterns when 'y' differ for |
1194 | 0 | // skeleton "MMMM". |
1195 | 0 | fInfo->getIntervalPattern(*extendedBestSkeleton,field,pattern,status); |
1196 | 0 | if ( pattern.isEmpty() && differenceInfo == 0 ) { |
1197 | 0 | // if there is no skeleton "yMMMM" defined, |
1198 | 0 | // look for the best match skeleton, for example: "yMMM" |
1199 | 0 | const UnicodeString* tmpBest = fInfo->getBestSkeleton( |
1200 | 0 | *extendedBestSkeleton, differenceInfo); |
1201 | 0 | if ( tmpBest != 0 && differenceInfo != -1 ) { |
1202 | 0 | fInfo->getIntervalPattern(*tmpBest, field, pattern, status); |
1203 | 0 | bestSkeleton = tmpBest; |
1204 | 0 | } |
1205 | 0 | } |
1206 | 0 | } |
1207 | 0 | } |
1208 | 0 | if ( !pattern.isEmpty() ) { |
1209 | 0 | if ( differenceInfo != 0 ) { |
1210 | 0 | UnicodeString adjustIntervalPattern; |
1211 | 0 | adjustFieldWidth(*skeleton, *bestSkeleton, pattern, differenceInfo, |
1212 | 0 | adjustIntervalPattern); |
1213 | 0 | setIntervalPattern(field, adjustIntervalPattern); |
1214 | 0 | } else { |
1215 | 0 | setIntervalPattern(field, pattern); |
1216 | 0 | } |
1217 | 0 | if ( extendedSkeleton && !extendedSkeleton->isEmpty() ) { |
1218 | 0 | return TRUE; |
1219 | 0 | } |
1220 | 0 | } |
1221 | 0 | return FALSE; |
1222 | 0 | } |
1223 | | |
1224 | | |
1225 | | |
1226 | | int32_t U_EXPORT2 |
1227 | 0 | DateIntervalFormat::splitPatternInto2Part(const UnicodeString& intervalPattern) { |
1228 | 0 | UBool inQuote = false; |
1229 | 0 | UChar prevCh = 0; |
1230 | 0 | int32_t count = 0; |
1231 | 0 |
|
1232 | 0 | /* repeatedPattern used to record whether a pattern has already seen. |
1233 | 0 | It is a pattern applies to first calendar if it is first time seen, |
1234 | 0 | otherwise, it is a pattern applies to the second calendar |
1235 | 0 | */ |
1236 | 0 | UBool patternRepeated[] = |
1237 | 0 | { |
1238 | 0 | // A B C D E F G H I J K L M N O |
1239 | 0 | 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, |
1240 | 0 | // P Q R S T U V W X Y Z |
1241 | 0 | 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, |
1242 | 0 | // a b c d e f g h i j k l m n o |
1243 | 0 | 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, |
1244 | 0 | // p q r s t u v w x y z |
1245 | 0 | 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 |
1246 | 0 | }; |
1247 | 0 |
|
1248 | 0 | int8_t PATTERN_CHAR_BASE = 0x41; |
1249 | 0 |
|
1250 | 0 | /* loop through the pattern string character by character looking for |
1251 | 0 | * the first repeated pattern letter, which breaks the interval pattern |
1252 | 0 | * into 2 parts. |
1253 | 0 | */ |
1254 | 0 | int32_t i; |
1255 | 0 | UBool foundRepetition = false; |
1256 | 0 | for (i = 0; i < intervalPattern.length(); ++i) { |
1257 | 0 | UChar ch = intervalPattern.charAt(i); |
1258 | 0 |
|
1259 | 0 | if (ch != prevCh && count > 0) { |
1260 | 0 | // check the repeativeness of pattern letter |
1261 | 0 | UBool repeated = patternRepeated[(int)(prevCh - PATTERN_CHAR_BASE)]; |
1262 | 0 | if ( repeated == FALSE ) { |
1263 | 0 | patternRepeated[prevCh - PATTERN_CHAR_BASE] = TRUE; |
1264 | 0 | } else { |
1265 | 0 | foundRepetition = true; |
1266 | 0 | break; |
1267 | 0 | } |
1268 | 0 | count = 0; |
1269 | 0 | } |
1270 | 0 | if (ch == 0x0027 /*'*/) { |
1271 | 0 | // Consecutive single quotes are a single quote literal, |
1272 | 0 | // either outside of quotes or between quotes |
1273 | 0 | if ((i+1) < intervalPattern.length() && |
1274 | 0 | intervalPattern.charAt(i+1) == 0x0027 /*'*/) { |
1275 | 0 | ++i; |
1276 | 0 | } else { |
1277 | 0 | inQuote = ! inQuote; |
1278 | 0 | } |
1279 | 0 | } |
1280 | 0 | else if (!inQuote && ((ch >= 0x0061 /*'a'*/ && ch <= 0x007A /*'z'*/) |
1281 | 0 | || (ch >= 0x0041 /*'A'*/ && ch <= 0x005A /*'Z'*/))) { |
1282 | 0 | // ch is a date-time pattern character |
1283 | 0 | prevCh = ch; |
1284 | 0 | ++count; |
1285 | 0 | } |
1286 | 0 | } |
1287 | 0 | // check last pattern char, distinguish |
1288 | 0 | // "dd MM" ( no repetition ), |
1289 | 0 | // "d-d"(last char repeated ), and |
1290 | 0 | // "d-d MM" ( repetition found ) |
1291 | 0 | if ( count > 0 && foundRepetition == FALSE ) { |
1292 | 0 | if ( patternRepeated[(int)(prevCh - PATTERN_CHAR_BASE)] == FALSE ) { |
1293 | 0 | count = 0; |
1294 | 0 | } |
1295 | 0 | } |
1296 | 0 | return (i - count); |
1297 | 0 | } |
1298 | | |
1299 | | static const UChar bracketedZero[] = {0x7B,0x30,0x7D}; |
1300 | | static const UChar bracketedOne[] = {0x7B,0x31,0x7D}; |
1301 | | |
1302 | | void |
1303 | | DateIntervalFormat::adjustPosition(UnicodeString& combiningPattern, // has {0} and {1} in it |
1304 | | UnicodeString& pat0, FieldPosition& pos0, // pattern and pos corresponding to {0} |
1305 | | UnicodeString& pat1, FieldPosition& pos1, // pattern and pos corresponding to {1} |
1306 | 0 | FieldPosition& posResult) { |
1307 | 0 | int32_t index0 = combiningPattern.indexOf(bracketedZero, 3, 0); |
1308 | 0 | int32_t index1 = combiningPattern.indexOf(bracketedOne, 3, 0); |
1309 | 0 | if (index0 < 0 || index1 < 0) { |
1310 | 0 | return; |
1311 | 0 | } |
1312 | 0 | int32_t placeholderLen = 3; // length of "{0}" or "{1}" |
1313 | 0 | if (index0 < index1) { |
1314 | 0 | if (pos0.getEndIndex() > 0) { |
1315 | 0 | posResult.setBeginIndex(pos0.getBeginIndex() + index0); |
1316 | 0 | posResult.setEndIndex(pos0.getEndIndex() + index0); |
1317 | 0 | } else if (pos1.getEndIndex() > 0) { |
1318 | 0 | // here index1 >= 3 |
1319 | 0 | index1 += pat0.length() - placeholderLen; // adjust for pat0 replacing {0} |
1320 | 0 | posResult.setBeginIndex(pos1.getBeginIndex() + index1); |
1321 | 0 | posResult.setEndIndex(pos1.getEndIndex() + index1); |
1322 | 0 | } |
1323 | 0 | } else { |
1324 | 0 | if (pos1.getEndIndex() > 0) { |
1325 | 0 | posResult.setBeginIndex(pos1.getBeginIndex() + index1); |
1326 | 0 | posResult.setEndIndex(pos1.getEndIndex() + index1); |
1327 | 0 | } else if (pos0.getEndIndex() > 0) { |
1328 | 0 | // here index0 >= 3 |
1329 | 0 | index0 += pat1.length() - placeholderLen; // adjust for pat1 replacing {1} |
1330 | 0 | posResult.setBeginIndex(pos0.getBeginIndex() + index0); |
1331 | 0 | posResult.setEndIndex(pos0.getEndIndex() + index0); |
1332 | 0 | } |
1333 | 0 | } |
1334 | 0 | } |
1335 | | |
1336 | | UnicodeString& |
1337 | | DateIntervalFormat::fallbackFormat(Calendar& fromCalendar, |
1338 | | Calendar& toCalendar, |
1339 | | UBool fromToOnSameDay, // new |
1340 | | UnicodeString& appendTo, |
1341 | | FieldPosition& pos, |
1342 | 0 | UErrorCode& status) const { |
1343 | 0 | if ( U_FAILURE(status) ) { |
1344 | 0 | return appendTo; |
1345 | 0 | } |
1346 | 0 | UnicodeString fullPattern; // for saving the pattern in fDateFormat |
1347 | 0 | UBool formatDatePlusTimeRange = (fromToOnSameDay && fDatePattern && fTimePattern); |
1348 | 0 | // the fall back |
1349 | 0 | if (formatDatePlusTimeRange) { |
1350 | 0 | fDateFormat->toPattern(fullPattern); // save current pattern, restore later |
1351 | 0 | fDateFormat->applyPattern(*fTimePattern); |
1352 | 0 | } |
1353 | 0 | FieldPosition otherPos; |
1354 | 0 | otherPos.setField(pos.getField()); |
1355 | 0 | UnicodeString earlierDate; |
1356 | 0 | fDateFormat->format(fromCalendar, earlierDate, pos); |
1357 | 0 | UnicodeString laterDate; |
1358 | 0 | fDateFormat->format(toCalendar, laterDate, otherPos); |
1359 | 0 | UnicodeString fallbackPattern; |
1360 | 0 | fInfo->getFallbackIntervalPattern(fallbackPattern); |
1361 | 0 | adjustPosition(fallbackPattern, earlierDate, pos, laterDate, otherPos, pos); |
1362 | 0 | UnicodeString fallbackRange; |
1363 | 0 | SimpleFormatter(fallbackPattern, 2, 2, status). |
1364 | 0 | format(earlierDate, laterDate, fallbackRange, status); |
1365 | 0 | if ( U_SUCCESS(status) && formatDatePlusTimeRange ) { |
1366 | 0 | // fallbackRange has just the time range, need to format the date part and combine that |
1367 | 0 | fDateFormat->applyPattern(*fDatePattern); |
1368 | 0 | UnicodeString datePortion; |
1369 | 0 | otherPos.setBeginIndex(0); |
1370 | 0 | otherPos.setEndIndex(0); |
1371 | 0 | fDateFormat->format(fromCalendar, datePortion, otherPos); |
1372 | 0 | adjustPosition(*fDateTimeFormat, fallbackRange, pos, datePortion, otherPos, pos); |
1373 | 0 | const UnicodeString *values[2] = { |
1374 | 0 | &fallbackRange, // {0} is time range |
1375 | 0 | &datePortion, // {1} is single date portion |
1376 | 0 | }; |
1377 | 0 | SimpleFormatter(*fDateTimeFormat, 2, 2, status). |
1378 | 0 | formatAndReplace(values, 2, fallbackRange, NULL, 0, status); |
1379 | 0 | } |
1380 | 0 | if ( U_SUCCESS(status) ) { |
1381 | 0 | appendTo.append(fallbackRange); |
1382 | 0 | } |
1383 | 0 | if (formatDatePlusTimeRange) { |
1384 | 0 | // restore full pattern |
1385 | 0 | fDateFormat->applyPattern(fullPattern); |
1386 | 0 | } |
1387 | 0 | return appendTo; |
1388 | 0 | } |
1389 | | |
1390 | | |
1391 | | |
1392 | | |
1393 | | UBool U_EXPORT2 |
1394 | | DateIntervalFormat::fieldExistsInSkeleton(UCalendarDateFields field, |
1395 | | const UnicodeString& skeleton) |
1396 | 0 | { |
1397 | 0 | const UChar fieldChar = fgCalendarFieldToPatternLetter[field]; |
1398 | 0 | return ( (skeleton.indexOf(fieldChar) == -1)?FALSE:TRUE ) ; |
1399 | 0 | } |
1400 | | |
1401 | | |
1402 | | |
1403 | | void U_EXPORT2 |
1404 | | DateIntervalFormat::adjustFieldWidth(const UnicodeString& inputSkeleton, |
1405 | | const UnicodeString& bestMatchSkeleton, |
1406 | | const UnicodeString& bestIntervalPattern, |
1407 | | int8_t differenceInfo, |
1408 | 0 | UnicodeString& adjustedPtn) { |
1409 | 0 | adjustedPtn = bestIntervalPattern; |
1410 | 0 | int32_t inputSkeletonFieldWidth[] = |
1411 | 0 | { |
1412 | 0 | // A B C D E F G H I J K L M N O |
1413 | 0 | 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, |
1414 | 0 | // P Q R S T U V W X Y Z |
1415 | 0 | 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, |
1416 | 0 | // a b c d e f g h i j k l m n o |
1417 | 0 | 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, |
1418 | 0 | // p q r s t u v w x y z |
1419 | 0 | 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 |
1420 | 0 | }; |
1421 | 0 |
|
1422 | 0 | int32_t bestMatchSkeletonFieldWidth[] = |
1423 | 0 | { |
1424 | 0 | // A B C D E F G H I J K L M N O |
1425 | 0 | 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, |
1426 | 0 | // P Q R S T U V W X Y Z |
1427 | 0 | 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, |
1428 | 0 | // a b c d e f g h i j k l m n o |
1429 | 0 | 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, |
1430 | 0 | // p q r s t u v w x y z |
1431 | 0 | 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 |
1432 | 0 | }; |
1433 | 0 |
|
1434 | 0 | DateIntervalInfo::parseSkeleton(inputSkeleton, inputSkeletonFieldWidth); |
1435 | 0 | DateIntervalInfo::parseSkeleton(bestMatchSkeleton, bestMatchSkeletonFieldWidth); |
1436 | 0 | if ( differenceInfo == 2 ) { |
1437 | 0 | adjustedPtn.findAndReplace(UnicodeString((UChar)0x76 /* v */), |
1438 | 0 | UnicodeString((UChar)0x7a /* z */)); |
1439 | 0 | } |
1440 | 0 |
|
1441 | 0 | UBool inQuote = false; |
1442 | 0 | UChar prevCh = 0; |
1443 | 0 | int32_t count = 0; |
1444 | 0 |
|
1445 | 0 | const int8_t PATTERN_CHAR_BASE = 0x41; |
1446 | 0 |
|
1447 | 0 | // loop through the pattern string character by character |
1448 | 0 | int32_t adjustedPtnLength = adjustedPtn.length(); |
1449 | 0 | int32_t i; |
1450 | 0 | for (i = 0; i < adjustedPtnLength; ++i) { |
1451 | 0 | UChar ch = adjustedPtn.charAt(i); |
1452 | 0 | if (ch != prevCh && count > 0) { |
1453 | 0 | // check the repeativeness of pattern letter |
1454 | 0 | UChar skeletonChar = prevCh; |
1455 | 0 | if ( skeletonChar == CAP_L ) { |
1456 | 0 | // there is no "L" (always be "M") in skeleton, |
1457 | 0 | // but there is "L" in pattern. |
1458 | 0 | // for skeleton "M+", the pattern might be "...L..." |
1459 | 0 | skeletonChar = CAP_M; |
1460 | 0 | } |
1461 | 0 | int32_t fieldCount = bestMatchSkeletonFieldWidth[(int)(skeletonChar - PATTERN_CHAR_BASE)]; |
1462 | 0 | int32_t inputFieldCount = inputSkeletonFieldWidth[(int)(skeletonChar - PATTERN_CHAR_BASE)]; |
1463 | 0 | if ( fieldCount == count && inputFieldCount > fieldCount ) { |
1464 | 0 | count = inputFieldCount - fieldCount; |
1465 | 0 | int32_t j; |
1466 | 0 | for ( j = 0; j < count; ++j ) { |
1467 | 0 | adjustedPtn.insert(i, prevCh); |
1468 | 0 | } |
1469 | 0 | i += count; |
1470 | 0 | adjustedPtnLength += count; |
1471 | 0 | } |
1472 | 0 | count = 0; |
1473 | 0 | } |
1474 | 0 | if (ch == 0x0027 /*'*/) { |
1475 | 0 | // Consecutive single quotes are a single quote literal, |
1476 | 0 | // either outside of quotes or between quotes |
1477 | 0 | if ((i+1) < adjustedPtn.length() && adjustedPtn.charAt(i+1) == 0x0027 /* ' */) { |
1478 | 0 | ++i; |
1479 | 0 | } else { |
1480 | 0 | inQuote = ! inQuote; |
1481 | 0 | } |
1482 | 0 | } |
1483 | 0 | else if ( ! inQuote && ((ch >= 0x0061 /*'a'*/ && ch <= 0x007A /*'z'*/) |
1484 | 0 | || (ch >= 0x0041 /*'A'*/ && ch <= 0x005A /*'Z'*/))) { |
1485 | 0 | // ch is a date-time pattern character |
1486 | 0 | prevCh = ch; |
1487 | 0 | ++count; |
1488 | 0 | } |
1489 | 0 | } |
1490 | 0 | if ( count > 0 ) { |
1491 | 0 | // last item |
1492 | 0 | // check the repeativeness of pattern letter |
1493 | 0 | UChar skeletonChar = prevCh; |
1494 | 0 | if ( skeletonChar == CAP_L ) { |
1495 | 0 | // there is no "L" (always be "M") in skeleton, |
1496 | 0 | // but there is "L" in pattern. |
1497 | 0 | // for skeleton "M+", the pattern might be "...L..." |
1498 | 0 | skeletonChar = CAP_M; |
1499 | 0 | } |
1500 | 0 | int32_t fieldCount = bestMatchSkeletonFieldWidth[(int)(skeletonChar - PATTERN_CHAR_BASE)]; |
1501 | 0 | int32_t inputFieldCount = inputSkeletonFieldWidth[(int)(skeletonChar - PATTERN_CHAR_BASE)]; |
1502 | 0 | if ( fieldCount == count && inputFieldCount > fieldCount ) { |
1503 | 0 | count = inputFieldCount - fieldCount; |
1504 | 0 | int32_t j; |
1505 | 0 | for ( j = 0; j < count; ++j ) { |
1506 | 0 | adjustedPtn.append(prevCh); |
1507 | 0 | } |
1508 | 0 | } |
1509 | 0 | } |
1510 | 0 | } |
1511 | | |
1512 | | |
1513 | | |
1514 | | void |
1515 | | DateIntervalFormat::concatSingleDate2TimeInterval(UnicodeString& format, |
1516 | | const UnicodeString& datePattern, |
1517 | | UCalendarDateFields field, |
1518 | 0 | UErrorCode& status) { |
1519 | 0 | // following should not set wrong status |
1520 | 0 | int32_t itvPtnIndex = DateIntervalInfo::calendarFieldToIntervalIndex(field, |
1521 | 0 | status); |
1522 | 0 | if ( U_FAILURE(status) ) { |
1523 | 0 | return; |
1524 | 0 | } |
1525 | 0 | PatternInfo& timeItvPtnInfo = fIntervalPatterns[itvPtnIndex]; |
1526 | 0 | if ( !timeItvPtnInfo.firstPart.isEmpty() ) { |
1527 | 0 | UnicodeString timeIntervalPattern(timeItvPtnInfo.firstPart); |
1528 | 0 | timeIntervalPattern.append(timeItvPtnInfo.secondPart); |
1529 | 0 | UnicodeString combinedPattern; |
1530 | 0 | SimpleFormatter(format, 2, 2, status). |
1531 | 0 | format(timeIntervalPattern, datePattern, combinedPattern, status); |
1532 | 0 | if ( U_FAILURE(status) ) { |
1533 | 0 | return; |
1534 | 0 | } |
1535 | 0 | setIntervalPattern(field, combinedPattern, timeItvPtnInfo.laterDateFirst); |
1536 | 0 | } |
1537 | 0 | // else: fall back |
1538 | 0 | // it should not happen if the interval format defined is valid |
1539 | 0 | } |
1540 | | |
1541 | | |
1542 | | |
1543 | | const UChar |
1544 | | DateIntervalFormat::fgCalendarFieldToPatternLetter[] = |
1545 | | { |
1546 | | /*GyM*/ CAP_G, LOW_Y, CAP_M, |
1547 | | /*wWd*/ LOW_W, CAP_W, LOW_D, |
1548 | | /*DEF*/ CAP_D, CAP_E, CAP_F, |
1549 | | /*ahH*/ LOW_A, LOW_H, CAP_H, |
1550 | | /*msS*/ LOW_M, LOW_S, CAP_S, // MINUTE, SECOND, MILLISECOND |
1551 | | /*z.Y*/ LOW_Z, SPACE, CAP_Y, // ZONE_OFFSET, DST_OFFSET, YEAR_WOY, |
1552 | | /*eug*/ LOW_E, LOW_U, LOW_G, // DOW_LOCAL, EXTENDED_YEAR, JULIAN_DAY, |
1553 | | /*A..*/ CAP_A, SPACE, SPACE, // MILLISECONDS_IN_DAY, IS_LEAP_MONTH, FIELD_COUNT |
1554 | | }; |
1555 | | |
1556 | | |
1557 | | U_NAMESPACE_END |
1558 | | |
1559 | | #endif |