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