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