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