]> git.saurik.com Git - apple/icu.git/blob - icuSources/i18n/dtitvfmt.cpp
ICU-461.18.tar.gz
[apple/icu.git] / icuSources / i18n / dtitvfmt.cpp
1 /*******************************************************************************
2 * Copyright (C) 2008-2010, 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/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 DateIntervalFormat::DateIntervalFormat(const Locale& locale,
420 DateIntervalInfo* dtItvInfo,
421 const UnicodeString* skeleton,
422 UErrorCode& status)
423 : fInfo(NULL),
424 fDateFormat(NULL),
425 fFromCalendar(NULL),
426 fToCalendar(NULL),
427 fDtpng(NULL)
428 {
429 if ( U_FAILURE(status) ) {
430 delete dtItvInfo;
431 return;
432 }
433 fDtpng = DateTimePatternGenerator::createInstance(locale, status);
434 SimpleDateFormat* dtfmt = createSDFPatternInstance(*skeleton, locale,
435 fDtpng, status);
436 if ( U_FAILURE(status) ) {
437 delete dtItvInfo;
438 delete fDtpng;
439 delete dtfmt;
440 return;
441 }
442 if ( dtfmt == NULL || dtItvInfo == NULL || fDtpng == NULL ) {
443 status = U_MEMORY_ALLOCATION_ERROR;
444 // safe to delete NULL
445 delete dtfmt;
446 delete dtItvInfo;
447 delete fDtpng;
448 return;
449 }
450 if ( skeleton ) {
451 fSkeleton = *skeleton;
452 }
453 fInfo = dtItvInfo;
454 fDateFormat = dtfmt;
455 if ( dtfmt->getCalendar() ) {
456 fFromCalendar = dtfmt->getCalendar()->clone();
457 fToCalendar = dtfmt->getCalendar()->clone();
458 } else {
459 fFromCalendar = NULL;
460 fToCalendar = NULL;
461 }
462 initializePattern(status);
463 }
464
465
466 SimpleDateFormat* U_EXPORT2
467 DateIntervalFormat::createSDFPatternInstance(const UnicodeString& skeleton,
468 const Locale& locale,
469 DateTimePatternGenerator* dtpng,
470 UErrorCode& status)
471 {
472 if ( U_FAILURE(status) ) {
473 return NULL;
474 }
475
476 const UnicodeString pattern = dtpng->getBestPattern(skeleton, status);
477 if ( U_FAILURE(status) ) {
478 return NULL;
479 }
480 SimpleDateFormat* dtfmt = new SimpleDateFormat(pattern, locale, status);
481 if ( U_FAILURE(status) ) {
482 delete dtfmt;
483 return NULL;
484 }
485 return dtfmt;
486 }
487
488
489 DateIntervalFormat* U_EXPORT2
490 DateIntervalFormat::create(const Locale& locale,
491 DateIntervalInfo* dtitvinf,
492 const UnicodeString* skeleton,
493 UErrorCode& status) {
494 DateIntervalFormat* f = new DateIntervalFormat(locale, dtitvinf,
495 skeleton, status);
496 if ( f == NULL ) {
497 status = U_MEMORY_ALLOCATION_ERROR;
498 delete dtitvinf;
499 } else if ( U_FAILURE(status) ) {
500 // safe to delete f, although nothing acutally is saved
501 delete f;
502 f = 0;
503 }
504 return f;
505 }
506
507
508
509 /**
510 * Initialize interval patterns locale to this formatter
511 *
512 * This code is a bit complicated since
513 * 1. the interval patterns saved in resource bundle files are interval
514 * patterns based on date or time only.
515 * It does not have interval patterns based on both date and time.
516 * Interval patterns on both date and time are algorithm generated.
517 *
518 * For example, it has interval patterns on skeleton "dMy" and "hm",
519 * but it does not have interval patterns on skeleton "dMyhm".
520 *
521 * The rule to genearte interval patterns for both date and time skeleton are
522 * 1) when the year, month, or day differs, concatenate the two original
523 * expressions with a separator between,
524 * For example, interval pattern from "Jan 10, 2007 10:10 am"
525 * to "Jan 11, 2007 10:10am" is
526 * "Jan 10, 2007 10:10 am - Jan 11, 2007 10:10am"
527 *
528 * 2) otherwise, present the date followed by the range expression
529 * for the time.
530 * For example, interval pattern from "Jan 10, 2007 10:10 am"
531 * to "Jan 10, 2007 11:10am" is
532 * "Jan 10, 2007 10:10 am - 11:10am"
533 *
534 * 2. even a pattern does not request a certion calendar field,
535 * the interval pattern needs to include such field if such fields are
536 * different between 2 dates.
537 * For example, a pattern/skeleton is "hm", but the interval pattern
538 * includes year, month, and date when year, month, and date differs.
539 *
540 * @param status output param set to success/failure code on exit
541 * @stable ICU 4.0
542 */
543 void
544 DateIntervalFormat::initializePattern(UErrorCode& status) {
545 if ( U_FAILURE(status) ) {
546 return;
547 }
548 const Locale& locale = fDateFormat->getSmpFmtLocale();
549 if ( fSkeleton.isEmpty() ) {
550 UnicodeString fullPattern;
551 fDateFormat->toPattern(fullPattern);
552 #ifdef DTITVFMT_DEBUG
553 char result[1000];
554 char result_1[1000];
555 char mesg[2000];
556 fSkeleton.extract(0, fSkeleton.length(), result, "UTF-8");
557 sprintf(mesg, "in getBestSkeleton: fSkeleton: %s; \n", result);
558 PRINTMESG(mesg)
559 #endif
560 // fSkeleton is already set by createDateIntervalInstance()
561 // or by createInstance(UnicodeString skeleton, .... )
562 fSkeleton = fDtpng->getSkeleton(fullPattern, status);
563 if ( U_FAILURE(status) ) {
564 return;
565 }
566 }
567
568 // initialize the fIntervalPattern ordering
569 int8_t i;
570 for ( i = 0; i < DateIntervalInfo::kIPI_MAX_INDEX; ++i ) {
571 fIntervalPatterns[i].laterDateFirst = fInfo->getDefaultOrder();
572 }
573
574 /* Check whether the skeleton is a combination of date and time.
575 * For the complication reason 1 explained above.
576 */
577 UnicodeString dateSkeleton;
578 UnicodeString timeSkeleton;
579 UnicodeString normalizedTimeSkeleton;
580 UnicodeString normalizedDateSkeleton;
581
582
583 /* the difference between time skeleton and normalizedTimeSkeleton are:
584 * 1. (Formerly, normalized time skeleton folded 'H' to 'h'; no longer true)
585 * 2. 'a' is omitted in normalized time skeleton.
586 * 3. there is only one appearance for 'h' or 'H', 'm','v', 'z' in normalized
587 * time skeleton
588 *
589 * The difference between date skeleton and normalizedDateSkeleton are:
590 * 1. both 'y' and 'd' appear only once in normalizeDateSkeleton
591 * 2. 'E' and 'EE' are normalized into 'EEE'
592 * 3. 'MM' is normalized into 'M'
593 */
594 getDateTimeSkeleton(fSkeleton, dateSkeleton, normalizedDateSkeleton,
595 timeSkeleton, normalizedTimeSkeleton);
596
597 #ifdef DTITVFMT_DEBUG
598 char result[1000];
599 char result_1[1000];
600 char mesg[2000];
601 fSkeleton.extract(0, fSkeleton.length(), result, "UTF-8");
602 sprintf(mesg, "in getBestSkeleton: fSkeleton: %s; \n", result);
603 PRINTMESG(mesg)
604 #endif
605
606
607 UBool found = setSeparateDateTimePtn(normalizedDateSkeleton,
608 normalizedTimeSkeleton);
609
610 if ( found == false ) {
611 // use fallback
612 // TODO: if user asks "m"(minute), but "d"(day) differ
613 if ( timeSkeleton.length() != 0 ) {
614 if ( dateSkeleton.length() == 0 ) {
615 // prefix with yMd
616 timeSkeleton.insert(0, gDateFormatSkeleton[DateFormat::kShort]);
617 UnicodeString pattern = fDtpng->getBestPattern(timeSkeleton, status);
618 if ( U_FAILURE(status) ) {
619 return;
620 }
621 // for fall back interval patterns,
622 // the first part of the pattern is empty,
623 // the second part of the pattern is the full-pattern
624 // should be used in fall-back.
625 setPatternInfo(UCAL_DATE, NULL, &pattern, fInfo->getDefaultOrder());
626 setPatternInfo(UCAL_MONTH, NULL, &pattern, fInfo->getDefaultOrder());
627 setPatternInfo(UCAL_YEAR, NULL, &pattern, fInfo->getDefaultOrder());
628 } else {
629 // TODO: fall back
630 }
631 } else {
632 // TODO: fall back
633 }
634 return;
635 } // end of skeleton not found
636 // interval patterns for skeleton are found in resource
637 if ( timeSkeleton.length() == 0 ) {
638 // done
639 } else if ( dateSkeleton.length() == 0 ) {
640 // prefix with yMd
641 timeSkeleton.insert(0, gDateFormatSkeleton[DateFormat::kShort]);
642 UnicodeString pattern = fDtpng->getBestPattern(timeSkeleton, status);
643 if ( U_FAILURE(status) ) {
644 return;
645 }
646 // for fall back interval patterns,
647 // the first part of the pattern is empty,
648 // the second part of the pattern is the full-pattern
649 // should be used in fall-back.
650 setPatternInfo(UCAL_DATE, NULL, &pattern, fInfo->getDefaultOrder());
651 setPatternInfo(UCAL_MONTH, NULL, &pattern, fInfo->getDefaultOrder());
652 setPatternInfo(UCAL_YEAR, NULL, &pattern, fInfo->getDefaultOrder());
653 } else {
654 /* if both present,
655 * 1) when the year, month, or day differs,
656 * concatenate the two original expressions with a separator between,
657 * 2) otherwise, present the date followed by the
658 * range expression for the time.
659 */
660 /*
661 * 1) when the year, month, or day differs,
662 * concatenate the two original expressions with a separator between,
663 */
664 // if field exists, use fall back
665 UnicodeString skeleton = fSkeleton;
666 if ( !fieldExistsInSkeleton(UCAL_DATE, dateSkeleton) ) {
667 // prefix skeleton with 'd'
668 skeleton.insert(0, LOW_D);
669 setFallbackPattern(UCAL_DATE, skeleton, status);
670 }
671 if ( !fieldExistsInSkeleton(UCAL_MONTH, dateSkeleton) ) {
672 // then prefix skeleton with 'M'
673 skeleton.insert(0, CAP_M);
674 setFallbackPattern(UCAL_MONTH, skeleton, status);
675 }
676 if ( !fieldExistsInSkeleton(UCAL_YEAR, dateSkeleton) ) {
677 // then prefix skeleton with 'y'
678 skeleton.insert(0, LOW_Y);
679 setFallbackPattern(UCAL_YEAR, skeleton, status);
680 }
681
682 /*
683 * 2) otherwise, present the date followed by the
684 * range expression for the time.
685 */
686 // Need the Date/Time pattern for concatnation the date with
687 // the time interval.
688 // The date/time pattern ( such as {0} {1} ) is saved in
689 // calendar, that is why need to get the CalendarData here.
690 CalendarData* calData = new CalendarData(locale, NULL, status);
691
692 if ( U_FAILURE(status) ) {
693 delete calData;
694 return;
695 }
696
697 if ( calData == NULL ) {
698 status = U_MEMORY_ALLOCATION_ERROR;
699 return;
700 }
701
702 const UResourceBundle* dateTimePatternsRes = calData->getByKey(
703 gDateTimePatternsTag, status);
704 int32_t dateTimeFormatLength;
705 const UChar* dateTimeFormat = ures_getStringByIndex(
706 dateTimePatternsRes,
707 (int32_t)DateFormat::kDateTime,
708 &dateTimeFormatLength, &status);
709 if ( U_FAILURE(status) ) {
710 return;
711 }
712
713 UnicodeString datePattern = fDtpng->getBestPattern(dateSkeleton, status);
714
715 concatSingleDate2TimeInterval(dateTimeFormat, dateTimeFormatLength,
716 datePattern, UCAL_AM_PM, status);
717 concatSingleDate2TimeInterval(dateTimeFormat, dateTimeFormatLength,
718 datePattern, UCAL_HOUR, status);
719 concatSingleDate2TimeInterval(dateTimeFormat, dateTimeFormatLength,
720 datePattern, UCAL_MINUTE, status);
721 delete calData;
722 }
723 }
724
725
726
727 void U_EXPORT2
728 DateIntervalFormat::getDateTimeSkeleton(const UnicodeString& skeleton,
729 UnicodeString& dateSkeleton,
730 UnicodeString& normalizedDateSkeleton,
731 UnicodeString& timeSkeleton,
732 UnicodeString& normalizedTimeSkeleton) {
733 // dateSkeleton follows the sequence of y*M*E*d*
734 // timeSkeleton follows the sequence of hm*[v|z]?
735 int32_t ECount = 0;
736 int32_t dCount = 0;
737 int32_t MCount = 0;
738 int32_t yCount = 0;
739 int32_t hCount = 0;
740 int32_t HCount = 0;
741 int32_t mCount = 0;
742 int32_t vCount = 0;
743 int32_t zCount = 0;
744 int32_t i;
745
746 for (i = 0; i < skeleton.length(); ++i) {
747 UChar ch = skeleton[i];
748 switch ( ch ) {
749 case CAP_E:
750 dateSkeleton.append(ch);
751 ++ECount;
752 break;
753 case LOW_D:
754 dateSkeleton.append(ch);
755 ++dCount;
756 break;
757 case CAP_M:
758 dateSkeleton.append(ch);
759 ++MCount;
760 break;
761 case LOW_Y:
762 dateSkeleton.append(ch);
763 ++yCount;
764 break;
765 case CAP_G:
766 case CAP_Y:
767 case LOW_U:
768 case CAP_Q:
769 case LOW_Q:
770 case CAP_L:
771 case LOW_L:
772 case CAP_W:
773 case LOW_W:
774 case CAP_D:
775 case CAP_F:
776 case LOW_G:
777 case LOW_E:
778 case LOW_C:
779 normalizedDateSkeleton.append(ch);
780 dateSkeleton.append(ch);
781 break;
782 case LOW_A:
783 // 'a' is implicitly handled
784 timeSkeleton.append(ch);
785 break;
786 case LOW_H:
787 timeSkeleton.append(ch);
788 ++hCount;
789 break;
790 case CAP_H:
791 timeSkeleton.append(ch);
792 ++HCount;
793 break;
794 case LOW_M:
795 timeSkeleton.append(ch);
796 ++mCount;
797 break;
798 case LOW_Z:
799 ++zCount;
800 timeSkeleton.append(ch);
801 break;
802 case LOW_V:
803 ++vCount;
804 timeSkeleton.append(ch);
805 break;
806 case CAP_V:
807 case CAP_Z:
808 case LOW_K:
809 case CAP_K:
810 case LOW_J:
811 case LOW_S:
812 case CAP_S:
813 case CAP_A:
814 timeSkeleton.append(ch);
815 normalizedTimeSkeleton.append(ch);
816 break;
817 }
818 }
819
820 /* generate normalized form for date*/
821 if ( yCount != 0 ) {
822 normalizedDateSkeleton.append(LOW_Y);
823 }
824 if ( MCount != 0 ) {
825 if ( MCount < 3 ) {
826 normalizedDateSkeleton.append(CAP_M);
827 } else {
828 int32_t i;
829 for ( i = 0; i < MCount && i < MAX_M_COUNT; ++i ) {
830 normalizedDateSkeleton.append(CAP_M);
831 }
832 }
833 }
834 if ( ECount != 0 ) {
835 if ( ECount <= 3 ) {
836 normalizedDateSkeleton.append(CAP_E);
837 } else {
838 int32_t i;
839 for ( i = 0; i < ECount && i < MAX_E_COUNT; ++i ) {
840 normalizedDateSkeleton.append(CAP_E);
841 }
842 }
843 }
844 if ( dCount != 0 ) {
845 normalizedDateSkeleton.append(LOW_D);
846 }
847
848 /* generate normalized form for time */
849 if ( HCount != 0 ) {
850 normalizedTimeSkeleton.append(CAP_H);
851 }
852 else if ( hCount != 0 ) {
853 normalizedTimeSkeleton.append(LOW_H);
854 }
855 if ( mCount != 0 ) {
856 normalizedTimeSkeleton.append(LOW_M);
857 }
858 if ( zCount != 0 ) {
859 normalizedTimeSkeleton.append(LOW_Z);
860 }
861 if ( vCount != 0 ) {
862 normalizedTimeSkeleton.append(LOW_V);
863 }
864 }
865
866
867 /**
868 * Generate date or time interval pattern from resource,
869 * and set them into the interval pattern locale to this formatter.
870 *
871 * It needs to handle the following:
872 * 1. need to adjust field width.
873 * For example, the interval patterns saved in DateIntervalInfo
874 * includes "dMMMy", but not "dMMMMy".
875 * Need to get interval patterns for dMMMMy from dMMMy.
876 * Another example, the interval patterns saved in DateIntervalInfo
877 * includes "hmv", but not "hmz".
878 * Need to get interval patterns for "hmz' from 'hmv'
879 *
880 * 2. there might be no pattern for 'y' differ for skeleton "Md",
881 * in order to get interval patterns for 'y' differ,
882 * need to look for it from skeleton 'yMd'
883 *
884 * @param dateSkeleton normalized date skeleton
885 * @param timeSkeleton normalized time skeleton
886 * @return whether the resource is found for the skeleton.
887 * TRUE if interval pattern found for the skeleton,
888 * FALSE otherwise.
889 * @stable ICU 4.0
890 */
891 UBool
892 DateIntervalFormat::setSeparateDateTimePtn(
893 const UnicodeString& dateSkeleton,
894 const UnicodeString& timeSkeleton) {
895 const UnicodeString* skeleton;
896 // if both date and time skeleton present,
897 // the final interval pattern might include time interval patterns
898 // ( when, am_pm, hour, minute differ ),
899 // but not date interval patterns ( when year, month, day differ ).
900 // For year/month/day differ, it falls back to fall-back pattern.
901 if ( timeSkeleton.length() != 0 ) {
902 skeleton = &timeSkeleton;
903 } else {
904 skeleton = &dateSkeleton;
905 }
906
907 /* interval patterns for skeleton "dMMMy" (but not "dMMMMy")
908 * are defined in resource,
909 * interval patterns for skeleton "dMMMMy" are calculated by
910 * 1. get the best match skeleton for "dMMMMy", which is "dMMMy"
911 * 2. get the interval patterns for "dMMMy",
912 * 3. extend "MMM" to "MMMM" in above interval patterns for "dMMMMy"
913 * getBestSkeleton() is step 1.
914 */
915 // best skeleton, and the difference information
916 int8_t differenceInfo = 0;
917 const UnicodeString* bestSkeleton = fInfo->getBestSkeleton(*skeleton,
918 differenceInfo);
919 /* best skeleton could be NULL.
920 For example: in "ca" resource file,
921 interval format is defined as following
922 intervalFormats{
923 fallback{"{0} - {1}"}
924 }
925 there is no skeletons/interval patterns defined,
926 and the best skeleton match could be NULL
927 */
928 if ( bestSkeleton == NULL ) {
929 return false;
930 }
931
932 // difference:
933 // 0 means the best matched skeleton is the same as input skeleton
934 // 1 means the fields are the same, but field width are different
935 // 2 means the only difference between fields are v/z,
936 // -1 means there are other fields difference
937 if ( differenceInfo == -1 ) {
938 // skeleton has different fields, not only v/z difference
939 return false;
940 }
941
942 if ( timeSkeleton.length() == 0 ) {
943 UnicodeString extendedSkeleton;
944 UnicodeString extendedBestSkeleton;
945 // only has date skeleton
946 setIntervalPattern(UCAL_DATE, skeleton, bestSkeleton, differenceInfo,
947 &extendedSkeleton, &extendedBestSkeleton);
948
949 UBool extended = setIntervalPattern(UCAL_MONTH, skeleton, bestSkeleton,
950 differenceInfo,
951 &extendedSkeleton, &extendedBestSkeleton);
952
953 if ( extended ) {
954 bestSkeleton = &extendedBestSkeleton;
955 skeleton = &extendedSkeleton;
956 }
957 setIntervalPattern(UCAL_YEAR, skeleton, bestSkeleton, differenceInfo,
958 &extendedSkeleton, &extendedBestSkeleton);
959 } else {
960 setIntervalPattern(UCAL_MINUTE, skeleton, bestSkeleton, differenceInfo);
961 setIntervalPattern(UCAL_HOUR, skeleton, bestSkeleton, differenceInfo);
962 setIntervalPattern(UCAL_AM_PM, skeleton, bestSkeleton, differenceInfo);
963 }
964 return true;
965 }
966
967
968
969 void
970 DateIntervalFormat::setFallbackPattern(UCalendarDateFields field,
971 const UnicodeString& skeleton,
972 UErrorCode& status) {
973 if ( U_FAILURE(status) ) {
974 return;
975 }
976 UnicodeString pattern = fDtpng->getBestPattern(skeleton, status);
977 if ( U_FAILURE(status) ) {
978 return;
979 }
980 setPatternInfo(field, NULL, &pattern, fInfo->getDefaultOrder());
981 }
982
983
984
985
986 void
987 DateIntervalFormat::setPatternInfo(UCalendarDateFields field,
988 const UnicodeString* firstPart,
989 const UnicodeString* secondPart,
990 UBool laterDateFirst) {
991 // for fall back interval patterns,
992 // the first part of the pattern is empty,
993 // the second part of the pattern is the full-pattern
994 // should be used in fall-back.
995 UErrorCode status = U_ZERO_ERROR;
996 // following should not set any wrong status.
997 int32_t itvPtnIndex = DateIntervalInfo::calendarFieldToIntervalIndex(field,
998 status);
999 if ( U_FAILURE(status) ) {
1000 return;
1001 }
1002 PatternInfo& ptn = fIntervalPatterns[itvPtnIndex];
1003 if ( firstPart ) {
1004 ptn.firstPart = *firstPart;
1005 }
1006 if ( secondPart ) {
1007 ptn.secondPart = *secondPart;
1008 }
1009 ptn.laterDateFirst = laterDateFirst;
1010 }
1011
1012 void
1013 DateIntervalFormat::setIntervalPattern(UCalendarDateFields field,
1014 const UnicodeString& intervalPattern) {
1015 UBool order = fInfo->getDefaultOrder();
1016 setIntervalPattern(field, intervalPattern, order);
1017 }
1018
1019
1020 void
1021 DateIntervalFormat::setIntervalPattern(UCalendarDateFields field,
1022 const UnicodeString& intervalPattern,
1023 UBool laterDateFirst) {
1024 const UnicodeString* pattern = &intervalPattern;
1025 UBool order = laterDateFirst;
1026 // check for "latestFirst:" or "earliestFirst:" prefix
1027 int8_t prefixLength = sizeof(gLaterFirstPrefix)/sizeof(gLaterFirstPrefix[0]);
1028 int8_t earliestFirstLength = sizeof(gEarlierFirstPrefix)/sizeof(gEarlierFirstPrefix[0]);
1029 UnicodeString realPattern;
1030 if ( intervalPattern.startsWith(gLaterFirstPrefix, prefixLength) ) {
1031 order = true;
1032 intervalPattern.extract(prefixLength,
1033 intervalPattern.length() - prefixLength,
1034 realPattern);
1035 pattern = &realPattern;
1036 } else if ( intervalPattern.startsWith(gEarlierFirstPrefix,
1037 earliestFirstLength) ) {
1038 order = false;
1039 intervalPattern.extract(earliestFirstLength,
1040 intervalPattern.length() - earliestFirstLength,
1041 realPattern);
1042 pattern = &realPattern;
1043 }
1044
1045 int32_t splitPoint = splitPatternInto2Part(*pattern);
1046
1047 UnicodeString firstPart;
1048 UnicodeString secondPart;
1049 pattern->extract(0, splitPoint, firstPart);
1050 if ( splitPoint < pattern->length() ) {
1051 pattern->extract(splitPoint, pattern->length()-splitPoint, secondPart);
1052 }
1053 setPatternInfo(field, &firstPart, &secondPart, order);
1054 }
1055
1056
1057
1058
1059 /**
1060 * Generate interval pattern from existing resource
1061 *
1062 * It not only save the interval patterns,
1063 * but also return the extended skeleton and its best match skeleton.
1064 *
1065 * @param field largest different calendar field
1066 * @param skeleton skeleton
1067 * @param bestSkeleton the best match skeleton which has interval pattern
1068 * defined in resource
1069 * @param differenceInfo the difference between skeleton and best skeleton
1070 * 0 means the best matched skeleton is the same as input skeleton
1071 * 1 means the fields are the same, but field width are different
1072 * 2 means the only difference between fields are v/z,
1073 * -1 means there are other fields difference
1074 *
1075 * @param extendedSkeleton extended skeleton
1076 * @param extendedBestSkeleton extended best match skeleton
1077 * @return whether the interval pattern is found
1078 * through extending skeleton or not.
1079 * TRUE if interval pattern is found by
1080 * extending skeleton, FALSE otherwise.
1081 * @stable ICU 4.0
1082 */
1083 UBool
1084 DateIntervalFormat::setIntervalPattern(UCalendarDateFields field,
1085 const UnicodeString* skeleton,
1086 const UnicodeString* bestSkeleton,
1087 int8_t differenceInfo,
1088 UnicodeString* extendedSkeleton,
1089 UnicodeString* extendedBestSkeleton) {
1090 UErrorCode status = U_ZERO_ERROR;
1091 // following getIntervalPattern() should not generate error status
1092 UnicodeString pattern;
1093 fInfo->getIntervalPattern(*bestSkeleton, field, pattern, status);
1094 if ( pattern.isEmpty() ) {
1095 // single date
1096 if ( SimpleDateFormat::isFieldUnitIgnored(*bestSkeleton, field) ) {
1097 // do nothing, format will handle it
1098 return false;
1099 }
1100
1101 // for 24 hour system, interval patterns in resource file
1102 // might not include pattern when am_pm differ,
1103 // which should be the same as hour differ.
1104 // add it here for simplicity
1105 if ( field == UCAL_AM_PM ) {
1106 fInfo->getIntervalPattern(*bestSkeleton, UCAL_HOUR, pattern,status);
1107 if ( !pattern.isEmpty() ) {
1108 setIntervalPattern(field, pattern);
1109 }
1110 return false;
1111 }
1112 // else, looking for pattern when 'y' differ for 'dMMMM' skeleton,
1113 // first, get best match pattern "MMMd",
1114 // since there is no pattern for 'y' differs for skeleton 'MMMd',
1115 // need to look for it from skeleton 'yMMMd',
1116 // if found, adjust field width in interval pattern from
1117 // "MMM" to "MMMM".
1118 UChar fieldLetter = fgCalendarFieldToPatternLetter[field];
1119 if ( extendedSkeleton ) {
1120 *extendedSkeleton = *skeleton;
1121 *extendedBestSkeleton = *bestSkeleton;
1122 extendedSkeleton->insert(0, fieldLetter);
1123 extendedBestSkeleton->insert(0, fieldLetter);
1124 // for example, looking for patterns when 'y' differ for
1125 // skeleton "MMMM".
1126 fInfo->getIntervalPattern(*extendedBestSkeleton,field,pattern,status);
1127 if ( pattern.isEmpty() && differenceInfo == 0 ) {
1128 // if there is no skeleton "yMMMM" defined,
1129 // look for the best match skeleton, for example: "yMMM"
1130 const UnicodeString* tmpBest = fInfo->getBestSkeleton(
1131 *extendedBestSkeleton, differenceInfo);
1132 if ( tmpBest != 0 && differenceInfo != -1 ) {
1133 fInfo->getIntervalPattern(*tmpBest, field, pattern, status);
1134 bestSkeleton = tmpBest;
1135 }
1136 }
1137 }
1138 }
1139 if ( !pattern.isEmpty() ) {
1140 if ( differenceInfo != 0 ) {
1141 UnicodeString adjustIntervalPattern;
1142 adjustFieldWidth(*skeleton, *bestSkeleton, pattern, differenceInfo,
1143 adjustIntervalPattern);
1144 setIntervalPattern(field, adjustIntervalPattern);
1145 } else {
1146 setIntervalPattern(field, pattern);
1147 }
1148 if ( extendedSkeleton && !extendedSkeleton->isEmpty() ) {
1149 return TRUE;
1150 }
1151 }
1152 return FALSE;
1153 }
1154
1155
1156
1157 int32_t U_EXPORT2
1158 DateIntervalFormat::splitPatternInto2Part(const UnicodeString& intervalPattern) {
1159 UBool inQuote = false;
1160 UChar prevCh = 0;
1161 int32_t count = 0;
1162
1163 /* repeatedPattern used to record whether a pattern has already seen.
1164 It is a pattern applies to first calendar if it is first time seen,
1165 otherwise, it is a pattern applies to the second calendar
1166 */
1167 UBool patternRepeated[] =
1168 {
1169 // A B C D E F G H I J K L M N O
1170 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
1171 // P Q R S T U V W X Y Z
1172 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
1173 // a b c d e f g h i j k l m n o
1174 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
1175 // p q r s t u v w x y z
1176 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0
1177 };
1178
1179 int8_t PATTERN_CHAR_BASE = 0x41;
1180
1181 /* loop through the pattern string character by character looking for
1182 * the first repeated pattern letter, which breaks the interval pattern
1183 * into 2 parts.
1184 */
1185 int32_t i;
1186 UBool foundRepetition = false;
1187 for (i = 0; i < intervalPattern.length(); ++i) {
1188 UChar ch = intervalPattern.charAt(i);
1189
1190 if (ch != prevCh && count > 0) {
1191 // check the repeativeness of pattern letter
1192 UBool repeated = patternRepeated[(int)(prevCh - PATTERN_CHAR_BASE)];
1193 if ( repeated == FALSE ) {
1194 patternRepeated[prevCh - PATTERN_CHAR_BASE] = TRUE;
1195 } else {
1196 foundRepetition = true;
1197 break;
1198 }
1199 count = 0;
1200 }
1201 if (ch == '\'') {
1202 // Consecutive single quotes are a single quote literal,
1203 // either outside of quotes or between quotes
1204 if ((i+1) < intervalPattern.length() &&
1205 intervalPattern.charAt(i+1) == '\'') {
1206 ++i;
1207 } else {
1208 inQuote = ! inQuote;
1209 }
1210 }
1211 else if (!inQuote && ((ch >= 0x0061 /*'a'*/ && ch <= 0x007A /*'z'*/)
1212 || (ch >= 0x0041 /*'A'*/ && ch <= 0x005A /*'Z'*/))) {
1213 // ch is a date-time pattern character
1214 prevCh = ch;
1215 ++count;
1216 }
1217 }
1218 // check last pattern char, distinguish
1219 // "dd MM" ( no repetition ),
1220 // "d-d"(last char repeated ), and
1221 // "d-d MM" ( repetition found )
1222 if ( count > 0 && foundRepetition == FALSE ) {
1223 if ( patternRepeated[(int)(prevCh - PATTERN_CHAR_BASE)] == FALSE ) {
1224 count = 0;
1225 }
1226 }
1227 return (i - count);
1228 }
1229
1230
1231
1232 UnicodeString&
1233 DateIntervalFormat::fallbackFormat(Calendar& fromCalendar,
1234 Calendar& toCalendar,
1235 UnicodeString& appendTo,
1236 FieldPosition& pos,
1237 UErrorCode& status) const {
1238 if ( U_FAILURE(status) ) {
1239 return appendTo;
1240 }
1241 // the fall back
1242 // no need delete earlierDate and laterDate since they are adopted
1243 UnicodeString* earlierDate = new UnicodeString();
1244 *earlierDate = fDateFormat->format(fromCalendar, *earlierDate, pos);
1245 UnicodeString* laterDate = new UnicodeString();
1246 *laterDate = fDateFormat->format(toCalendar, *laterDate, pos);
1247 UnicodeString fallbackPattern;
1248 fInfo->getFallbackIntervalPattern(fallbackPattern);
1249 Formattable fmtArray[2];
1250 fmtArray[0].adoptString(earlierDate);
1251 fmtArray[1].adoptString(laterDate);
1252
1253 UnicodeString fallback;
1254 MessageFormat::format(fallbackPattern, fmtArray, 2, fallback, status);
1255 if ( U_SUCCESS(status) ) {
1256 appendTo.append(fallback);
1257 }
1258 return appendTo;
1259 }
1260
1261
1262
1263
1264 UBool U_EXPORT2
1265 DateIntervalFormat::fieldExistsInSkeleton(UCalendarDateFields field,
1266 const UnicodeString& skeleton)
1267 {
1268 const UChar fieldChar = fgCalendarFieldToPatternLetter[field];
1269 return ( (skeleton.indexOf(fieldChar) == -1)?FALSE:TRUE ) ;
1270 }
1271
1272
1273
1274 void U_EXPORT2
1275 DateIntervalFormat::adjustFieldWidth(const UnicodeString& inputSkeleton,
1276 const UnicodeString& bestMatchSkeleton,
1277 const UnicodeString& bestIntervalPattern,
1278 int8_t differenceInfo,
1279 UnicodeString& adjustedPtn) {
1280 adjustedPtn = bestIntervalPattern;
1281 int32_t inputSkeletonFieldWidth[] =
1282 {
1283 // A B C D E F G H I J K L M N O
1284 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
1285 // P Q R S T U V W X Y Z
1286 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
1287 // a b c d e f g h i j k l m n o
1288 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
1289 // p q r s t u v w x y z
1290 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0
1291 };
1292
1293 int32_t bestMatchSkeletonFieldWidth[] =
1294 {
1295 // A B C D E F G H I J K L M N O
1296 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
1297 // P Q R S T U V W X Y Z
1298 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
1299 // a b c d e f g h i j k l m n o
1300 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
1301 // p q r s t u v w x y z
1302 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0
1303 };
1304
1305 DateIntervalInfo::parseSkeleton(inputSkeleton, inputSkeletonFieldWidth);
1306 DateIntervalInfo::parseSkeleton(bestMatchSkeleton, bestMatchSkeletonFieldWidth);
1307 if ( differenceInfo == 2 ) {
1308 adjustedPtn.findAndReplace("v", "z");
1309 }
1310
1311 UBool inQuote = false;
1312 UChar prevCh = 0;
1313 int32_t count = 0;
1314
1315 const int8_t PATTERN_CHAR_BASE = 0x41;
1316
1317 // loop through the pattern string character by character
1318 int32_t adjustedPtnLength = adjustedPtn.length();
1319 int32_t i;
1320 for (i = 0; i < adjustedPtnLength; ++i) {
1321 UChar ch = adjustedPtn.charAt(i);
1322 if (ch != prevCh && count > 0) {
1323 // check the repeativeness of pattern letter
1324 UChar skeletonChar = prevCh;
1325 if ( skeletonChar == CAP_L ) {
1326 // there is no "L" (always be "M") in skeleton,
1327 // but there is "L" in pattern.
1328 // for skeleton "M+", the pattern might be "...L..."
1329 skeletonChar = CAP_M;
1330 }
1331 int32_t fieldCount = bestMatchSkeletonFieldWidth[(int)(skeletonChar - PATTERN_CHAR_BASE)];
1332 int32_t inputFieldCount = inputSkeletonFieldWidth[(int)(skeletonChar - PATTERN_CHAR_BASE)];
1333 if ( fieldCount == count && inputFieldCount > fieldCount ) {
1334 count = inputFieldCount - fieldCount;
1335 int32_t j;
1336 for ( j = 0; j < count; ++j ) {
1337 adjustedPtn.insert(i, prevCh);
1338 }
1339 i += count;
1340 adjustedPtnLength += count;
1341 }
1342 count = 0;
1343 }
1344 if (ch == '\'') {
1345 // Consecutive single quotes are a single quote literal,
1346 // either outside of quotes or between quotes
1347 if ((i+1) < adjustedPtn.length() && adjustedPtn.charAt(i+1) == '\'') {
1348 ++i;
1349 } else {
1350 inQuote = ! inQuote;
1351 }
1352 }
1353 else if ( ! inQuote && ((ch >= 0x0061 /*'a'*/ && ch <= 0x007A /*'z'*/)
1354 || (ch >= 0x0041 /*'A'*/ && ch <= 0x005A /*'Z'*/))) {
1355 // ch is a date-time pattern character
1356 prevCh = ch;
1357 ++count;
1358 }
1359 }
1360 if ( count > 0 ) {
1361 // last item
1362 // check the repeativeness of pattern letter
1363 UChar skeletonChar = prevCh;
1364 if ( skeletonChar == CAP_L ) {
1365 // there is no "L" (always be "M") in skeleton,
1366 // but there is "L" in pattern.
1367 // for skeleton "M+", the pattern might be "...L..."
1368 skeletonChar = CAP_M;
1369 }
1370 int32_t fieldCount = bestMatchSkeletonFieldWidth[(int)(skeletonChar - PATTERN_CHAR_BASE)];
1371 int32_t inputFieldCount = inputSkeletonFieldWidth[(int)(skeletonChar - PATTERN_CHAR_BASE)];
1372 if ( fieldCount == count && inputFieldCount > fieldCount ) {
1373 count = inputFieldCount - fieldCount;
1374 int32_t j;
1375 for ( j = 0; j < count; ++j ) {
1376 adjustedPtn.append(prevCh);
1377 }
1378 }
1379 }
1380 }
1381
1382
1383
1384 void
1385 DateIntervalFormat::concatSingleDate2TimeInterval(const UChar* format,
1386 int32_t formatLen,
1387 const UnicodeString& datePattern,
1388 UCalendarDateFields field,
1389 UErrorCode& status) {
1390 // following should not set wrong status
1391 int32_t itvPtnIndex = DateIntervalInfo::calendarFieldToIntervalIndex(field,
1392 status);
1393 if ( U_FAILURE(status) ) {
1394 return;
1395 }
1396 PatternInfo& timeItvPtnInfo = fIntervalPatterns[itvPtnIndex];
1397 if ( !timeItvPtnInfo.firstPart.isEmpty() ) {
1398 // UnicodeString allocated here is adopted, so no need to delete
1399 UnicodeString* timeIntervalPattern = new UnicodeString(timeItvPtnInfo.firstPart);
1400 timeIntervalPattern->append(timeItvPtnInfo.secondPart);
1401 UnicodeString* dateStr = new UnicodeString(datePattern);
1402 Formattable fmtArray[2];
1403 fmtArray[0].adoptString(timeIntervalPattern);
1404 fmtArray[1].adoptString(dateStr);
1405 UnicodeString combinedPattern;
1406 MessageFormat::format(UnicodeString(TRUE, format, formatLen),
1407 fmtArray, 2, combinedPattern, status);
1408 if ( U_FAILURE(status) ) {
1409 return;
1410 }
1411 setIntervalPattern(field, combinedPattern, timeItvPtnInfo.laterDateFirst);
1412 }
1413 // else: fall back
1414 // it should not happen if the interval format defined is valid
1415 }
1416
1417
1418
1419 const UChar
1420 DateIntervalFormat::fgCalendarFieldToPatternLetter[] =
1421 {
1422 /*GyM*/ CAP_G, LOW_Y, CAP_M,
1423 /*wWd*/ LOW_W, CAP_W, LOW_D,
1424 /*DEF*/ CAP_D, CAP_E, CAP_F,
1425 /*ahH*/ LOW_A, LOW_H, CAP_H,
1426 /*m..*/ LOW_M,
1427 };
1428
1429
1430 U_NAMESPACE_END
1431
1432 #endif