1 /*******************************************************************************
2 * Copyright (C) 2008-2011, International Business Machines Corporation and
3 * others. All Rights Reserved.
4 *******************************************************************************
8 *******************************************************************************
11 #include <typeinfo> // for 'typeid' to work
13 #include "unicode/dtitvfmt.h"
15 #if !UCONFIG_NO_FORMATTING
17 //TODO: put in compilation
18 //#define DTITVFMT_DEBUG 1
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"
40 #define PRINTMESG(msg) { std::cout << "(" << __FILE__ << ":" << __LINE__ << ") " << msg << "\n"; }
44 static const UChar gDateFormatSkeleton
[][11] = {
46 {LOW_Y
, CAP_M
, CAP_M
, CAP_M
, CAP_M
, CAP_E
, CAP_E
, CAP_E
, CAP_E
, LOW_D
, 0},
48 {LOW_Y
, CAP_M
, CAP_M
, CAP_M
, CAP_M
, LOW_D
, 0},
50 {LOW_Y
, CAP_M
, CAP_M
, CAP_M
, LOW_D
, 0},
52 {LOW_Y
, CAP_M
, LOW_D
, 0} };
55 static const char gDateTimePatternsTag
[]="DateTimePatterns";
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
};
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
};
65 UOBJECT_DEFINE_RTTI_IMPLEMENTATION(DateIntervalFormat
)
69 DateIntervalFormat
* U_EXPORT2
70 DateIntervalFormat::createInstance(const UnicodeString
& skeleton
,
72 return createInstance(skeleton
, Locale::getDefault(), status
);
76 DateIntervalFormat
* U_EXPORT2
77 DateIntervalFormat::createInstance(const UnicodeString
& skeleton
,
84 skeleton
.extract(0, skeleton
.length(), result
, "UTF-8");
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
);
92 DateIntervalInfo
* dtitvinf
= new DateIntervalInfo(locale
, status
);
93 return create(locale
, dtitvinf
, &skeleton
, status
);
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
);
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
);
116 DateIntervalFormat::DateIntervalFormat()
122 fMinimizeType(UDTITVFMT_MINIMIZE_NONE
)
126 DateIntervalFormat::DateIntervalFormat(const DateIntervalFormat
& itvfmt
)
133 fMinimizeType(UDTITVFMT_MINIMIZE_NONE
) {
139 DateIntervalFormat::operator=(const DateIntervalFormat
& itvfmt
) {
140 if ( this != &itvfmt
) {
143 delete fFromCalendar
;
146 if ( itvfmt
.fDateFormat
) {
147 fDateFormat
= (SimpleDateFormat
*)itvfmt
.fDateFormat
->clone();
151 if ( itvfmt
.fInfo
) {
152 fInfo
= itvfmt
.fInfo
->clone();
156 if ( itvfmt
.fFromCalendar
) {
157 fFromCalendar
= itvfmt
.fFromCalendar
->clone();
159 fFromCalendar
= NULL
;
161 if ( itvfmt
.fToCalendar
) {
162 fToCalendar
= itvfmt
.fToCalendar
->clone();
166 fSkeleton
= itvfmt
.fSkeleton
;
168 for ( i
= 0; i
< DateIntervalInfo::kIPI_MAX_INDEX
; ++i
) {
169 fIntervalPatterns
[i
] = itvfmt
.fIntervalPatterns
[i
];
172 fDtpng
= itvfmt
.fDtpng
->clone();
179 DateIntervalFormat::~DateIntervalFormat() {
182 delete fFromCalendar
;
189 DateIntervalFormat::clone(void) const {
190 return new DateIntervalFormat(*this);
195 DateIntervalFormat::operator==(const Format
& other
) const {
196 if (typeid(*this) == typeid(other
)) {
197 const DateIntervalFormat
* fmt
= (DateIntervalFormat
*)&other
;
198 #ifdef DTITVFMT_DEBUG
200 equal
= (this == fmt
);
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
);
209 res
= ( this == fmt
) ||
210 ( Format::operator==(other
) &&
212 ( *fInfo
== *fmt
->fInfo
) &&
214 ( *fDateFormat
== *fmt
->fDateFormat
) &&
216 fFromCalendar
->isEquivalentTo(*fmt
->fFromCalendar
) &&
218 fToCalendar
->isEquivalentTo(*fmt
->fToCalendar
) &&
219 fSkeleton
== fmt
->fSkeleton
&&
221 (*fDtpng
== *fmt
->fDtpng
) );
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
) ;
239 DateIntervalFormat::format(const Formattable
& obj
,
240 UnicodeString
& appendTo
,
241 FieldPosition
& fieldPosition
,
242 UErrorCode
& status
) const {
243 if ( U_FAILURE(status
) ) {
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
);
254 status
= U_ILLEGAL_ARGUMENT_ERROR
;
260 DateIntervalFormat::format(const DateInterval
* dtInterval
,
261 UnicodeString
& appendTo
,
262 FieldPosition
& fieldPosition
,
263 UErrorCode
& status
) const {
264 if ( U_FAILURE(status
) ) {
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
);
281 DateIntervalFormat::format(Calendar
& fromCalendar
,
282 Calendar
& toCalendar
,
283 UnicodeString
& appendTo
,
285 UErrorCode
& status
) const {
286 if ( U_FAILURE(status
) ) {
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
;
297 // First, find the largest different calendar field.
298 UCalendarDateFields field
= UCAL_FIELD_COUNT
;
300 if ( fromCalendar
.get(UCAL_ERA
,status
) != toCalendar
.get(UCAL_ERA
,status
)) {
302 } else if ( fromCalendar
.get(UCAL_YEAR
, status
) !=
303 toCalendar
.get(UCAL_YEAR
, status
) ) {
305 } else if ( fromCalendar
.get(UCAL_MONTH
, status
) !=
306 toCalendar
.get(UCAL_MONTH
, status
) ) {
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
) {
318 fromCalendar
.setTime(fromDate
, status
);
320 } else if ( fromCalendar
.get(UCAL_DATE
, status
) !=
321 toCalendar
.get(UCAL_DATE
, status
) ) {
323 } else if ( fromCalendar
.get(UCAL_AM_PM
, status
) !=
324 toCalendar
.get(UCAL_AM_PM
, status
) ) {
326 } else if ( fromCalendar
.get(UCAL_HOUR
, status
) !=
327 toCalendar
.get(UCAL_HOUR
, status
) ) {
329 } else if ( fromCalendar
.get(UCAL_MINUTE
, status
) !=
330 toCalendar
.get(UCAL_MINUTE
, status
) ) {
334 if ( U_FAILURE(status
) ) {
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.
341 return fDateFormat
->format(fromCalendar
, appendTo
, pos
);
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
,
348 const PatternInfo
& intervalPattern
= fIntervalPatterns
[itvPtnIndex
];
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.
357 return fDateFormat
->format(fromCalendar
, appendTo
, pos
);
359 return fallbackFormat(fromCalendar
, toCalendar
, appendTo
, pos
, status
);
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() ) {
366 UnicodeString originalPattern
;
367 fDateFormat
->toPattern(originalPattern
);
368 fDateFormat
->applyPattern(intervalPattern
.secondPart
);
369 appendTo
= fallbackFormat(fromCalendar
, toCalendar
, appendTo
, pos
, status
);
370 fDateFormat
->applyPattern(originalPattern
);
375 if ( intervalPattern
.laterDateFirst
) {
376 firstCal
= &toCalendar
;
377 secondCal
= &fromCalendar
;
379 firstCal
= &fromCalendar
;
380 secondCal
= &toCalendar
;
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
);
392 fDateFormat
->applyPattern(originalPattern
);
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
410 const DateIntervalInfo
*
411 DateIntervalFormat::getDateIntervalInfo() const {
417 DateIntervalFormat::setDateIntervalInfo(const DateIntervalInfo
& newItvPattern
,
418 UErrorCode
& status
) {
420 fInfo
= new DateIntervalInfo(newItvPattern
);
422 initializePattern(status
);
429 DateIntervalFormat::getDateFormat() const {
435 DateIntervalFormat::adoptTimeZone(TimeZone
* zone
)
437 if (fDateFormat
!= NULL
) {
438 fDateFormat
->adoptTimeZone(zone
);
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).
445 fFromCalendar
->setTimeZone(*zone
);
448 fToCalendar
->setTimeZone(*zone
);
453 DateIntervalFormat::setTimeZone(const TimeZone
& zone
)
455 if (fDateFormat
!= NULL
) {
456 fDateFormat
->setTimeZone(zone
);
458 // The fDateFormat has the master calendar for the DateIntervalFormat;
459 // fFromCalendar and fToCalendar are internal work clones of that calendar.
461 fFromCalendar
->setTimeZone(zone
);
464 fToCalendar
->setTimeZone(zone
);
469 DateIntervalFormat::getTimeZone() const
471 if (fDateFormat
!= NULL
) {
472 return fDateFormat
->getTimeZone();
474 // If fDateFormat is NULL (unexpected), create default timezone.
475 return *(TimeZone::createDefault());
479 DateIntervalFormat::setAttribute(UDateIntervalFormatAttribute attr
,
480 UDateIntervalFormatAttributeValue value
,
483 if ( U_FAILURE(status
) ) {
486 if (attr
== UDTITVFMT_MINIMIZE_TYPE
) {
487 fMinimizeType
= value
;
489 status
= U_ILLEGAL_ARGUMENT_ERROR
;
493 DateIntervalFormat::DateIntervalFormat(const Locale
& locale
,
494 DateIntervalInfo
* dtItvInfo
,
495 const UnicodeString
* skeleton
,
502 fMinimizeType(UDTITVFMT_MINIMIZE_NONE
)
504 if ( U_FAILURE(status
) ) {
508 fDtpng
= DateTimePatternGenerator::createInstance(locale
, status
);
509 SimpleDateFormat
* dtfmt
= createSDFPatternInstance(*skeleton
, locale
,
511 if ( U_FAILURE(status
) ) {
517 if ( dtfmt
== NULL
|| dtItvInfo
== NULL
|| fDtpng
== NULL
) {
518 status
= U_MEMORY_ALLOCATION_ERROR
;
519 // safe to delete NULL
526 fSkeleton
= *skeleton
;
530 if ( dtfmt
->getCalendar() ) {
531 fFromCalendar
= dtfmt
->getCalendar()->clone();
532 fToCalendar
= dtfmt
->getCalendar()->clone();
534 fFromCalendar
= NULL
;
537 initializePattern(status
);
541 SimpleDateFormat
* U_EXPORT2
542 DateIntervalFormat::createSDFPatternInstance(const UnicodeString
& skeleton
,
543 const Locale
& locale
,
544 DateTimePatternGenerator
* dtpng
,
547 if ( U_FAILURE(status
) ) {
551 const UnicodeString pattern
= dtpng
->getBestPattern(skeleton
, status
);
552 if ( U_FAILURE(status
) ) {
555 SimpleDateFormat
* dtfmt
= new SimpleDateFormat(pattern
, locale
, status
);
556 if ( U_FAILURE(status
) ) {
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
,
572 status
= U_MEMORY_ALLOCATION_ERROR
;
574 } else if ( U_FAILURE(status
) ) {
575 // safe to delete f, although nothing acutally is saved
585 * Initialize interval patterns locale to this formatter
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.
593 * For example, it has interval patterns on skeleton "dMy" and "hm",
594 * but it does not have interval patterns on skeleton "dMyhm".
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"
603 * 2) otherwise, present the date followed by the range expression
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"
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.
615 * @param status output param set to success/failure code on exit
619 DateIntervalFormat::initializePattern(UErrorCode
& status
) {
620 if ( U_FAILURE(status
) ) {
623 const Locale
& locale
= fDateFormat
->getSmpFmtLocale();
624 if ( fSkeleton
.isEmpty() ) {
625 UnicodeString fullPattern
;
626 fDateFormat
->toPattern(fullPattern
);
627 #ifdef DTITVFMT_DEBUG
631 fSkeleton
.extract(0, fSkeleton
.length(), result
, "UTF-8");
632 sprintf(mesg
, "in getBestSkeleton: fSkeleton: %s; \n", result
);
635 // fSkeleton is already set by createDateIntervalInstance()
636 // or by createInstance(UnicodeString skeleton, .... )
637 fSkeleton
= fDtpng
->getSkeleton(fullPattern
, status
);
638 if ( U_FAILURE(status
) ) {
643 // initialize the fIntervalPattern ordering
645 for ( i
= 0; i
< DateIntervalInfo::kIPI_MAX_INDEX
; ++i
) {
646 fIntervalPatterns
[i
].laterDateFirst
= fInfo
->getDefaultOrder();
649 /* Check whether the skeleton is a combination of date and time.
650 * For the complication reason 1 explained above.
652 UnicodeString dateSkeleton
;
653 UnicodeString timeSkeleton
;
654 UnicodeString normalizedTimeSkeleton
;
655 UnicodeString normalizedDateSkeleton
;
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
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'
669 getDateTimeSkeleton(fSkeleton
, dateSkeleton
, normalizedDateSkeleton
,
670 timeSkeleton
, normalizedTimeSkeleton
);
672 #ifdef DTITVFMT_DEBUG
676 fSkeleton
.extract(0, fSkeleton
.length(), result
, "UTF-8");
677 sprintf(mesg
, "in getBestSkeleton: fSkeleton: %s; \n", result
);
682 UBool found
= setSeparateDateTimePtn(normalizedDateSkeleton
,
683 normalizedTimeSkeleton
);
685 if ( found
== false ) {
687 // TODO: if user asks "m"(minute), but "d"(day) differ
688 if ( timeSkeleton
.length() != 0 ) {
689 if ( dateSkeleton
.length() == 0 ) {
691 timeSkeleton
.insert(0, gDateFormatSkeleton
[DateFormat::kShort
], -1);
692 UnicodeString pattern
= fDtpng
->getBestPattern(timeSkeleton
, status
);
693 if ( U_FAILURE(status
) ) {
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());
710 } // end of skeleton not found
711 // interval patterns for skeleton are found in resource
712 if ( timeSkeleton
.length() == 0 ) {
714 } else if ( dateSkeleton
.length() == 0 ) {
716 timeSkeleton
.insert(0, gDateFormatSkeleton
[DateFormat::kShort
], -1);
717 UnicodeString pattern
= fDtpng
->getBestPattern(timeSkeleton
, status
);
718 if ( U_FAILURE(status
) ) {
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());
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.
736 * 1) when the year, month, or day differs,
737 * concatenate the two original expressions with a separator between,
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
);
746 if ( !fieldExistsInSkeleton(UCAL_MONTH
, dateSkeleton
) ) {
747 // then prefix skeleton with 'M'
748 skeleton
.insert(0, CAP_M
);
749 setFallbackPattern(UCAL_MONTH
, skeleton
, status
);
751 if ( !fieldExistsInSkeleton(UCAL_YEAR
, dateSkeleton
) ) {
752 // then prefix skeleton with 'y'
753 skeleton
.insert(0, LOW_Y
);
754 setFallbackPattern(UCAL_YEAR
, skeleton
, status
);
758 * 2) otherwise, present the date followed by the
759 * range expression for the time.
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
);
767 if ( U_FAILURE(status
) ) {
772 if ( calData
== NULL
) {
773 status
= U_MEMORY_ALLOCATION_ERROR
;
777 const UResourceBundle
* dateTimePatternsRes
= calData
->getByKey(
778 gDateTimePatternsTag
, status
);
779 int32_t dateTimeFormatLength
;
780 const UChar
* dateTimeFormat
= ures_getStringByIndex(
782 (int32_t)DateFormat::kDateTime
,
783 &dateTimeFormatLength
, &status
);
784 if ( U_FAILURE(status
) ) {
788 UnicodeString datePattern
= fDtpng
->getBestPattern(dateSkeleton
, status
);
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
);
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]?
821 for (i
= 0; i
< skeleton
.length(); ++i
) {
822 UChar ch
= skeleton
[i
];
825 dateSkeleton
.append(ch
);
829 dateSkeleton
.append(ch
);
833 dateSkeleton
.append(ch
);
837 dateSkeleton
.append(ch
);
854 normalizedDateSkeleton
.append(ch
);
855 dateSkeleton
.append(ch
);
858 // 'a' is implicitly handled
859 timeSkeleton
.append(ch
);
862 timeSkeleton
.append(ch
);
866 timeSkeleton
.append(ch
);
870 timeSkeleton
.append(ch
);
875 timeSkeleton
.append(ch
);
879 timeSkeleton
.append(ch
);
889 timeSkeleton
.append(ch
);
890 normalizedTimeSkeleton
.append(ch
);
895 /* generate normalized form for date*/
897 normalizedDateSkeleton
.append(LOW_Y
);
901 normalizedDateSkeleton
.append(CAP_M
);
904 for ( i
= 0; i
< MCount
&& i
< MAX_M_COUNT
; ++i
) {
905 normalizedDateSkeleton
.append(CAP_M
);
911 normalizedDateSkeleton
.append(CAP_E
);
914 for ( i
= 0; i
< ECount
&& i
< MAX_E_COUNT
; ++i
) {
915 normalizedDateSkeleton
.append(CAP_E
);
920 normalizedDateSkeleton
.append(LOW_D
);
923 /* generate normalized form for time */
925 normalizedTimeSkeleton
.append(CAP_H
);
927 else if ( hCount
!= 0 ) {
928 normalizedTimeSkeleton
.append(LOW_H
);
931 normalizedTimeSkeleton
.append(LOW_M
);
934 normalizedTimeSkeleton
.append(LOW_Z
);
937 normalizedTimeSkeleton
.append(LOW_V
);
943 * Generate date or time interval pattern from resource,
944 * and set them into the interval pattern locale to this formatter.
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'
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'
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,
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
;
979 skeleton
= &dateSkeleton
;
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.
990 // best skeleton, and the difference information
991 int8_t differenceInfo
= 0;
992 const UnicodeString
* bestSkeleton
= fInfo
->getBestSkeleton(*skeleton
,
994 /* best skeleton could be NULL.
995 For example: in "ca" resource file,
996 interval format is defined as following
998 fallback{"{0} - {1}"}
1000 there is no skeletons/interval patterns defined,
1001 and the best skeleton match could be NULL
1003 if ( bestSkeleton
== NULL
) {
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
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
);
1024 UBool extended
= setIntervalPattern(UCAL_MONTH
, skeleton
, bestSkeleton
,
1026 &extendedSkeleton
, &extendedBestSkeleton
);
1029 bestSkeleton
= &extendedBestSkeleton
;
1030 skeleton
= &extendedSkeleton
;
1032 setIntervalPattern(UCAL_YEAR
, skeleton
, bestSkeleton
, differenceInfo
,
1033 &extendedSkeleton
, &extendedBestSkeleton
);
1035 setIntervalPattern(UCAL_MINUTE
, skeleton
, bestSkeleton
, differenceInfo
);
1036 setIntervalPattern(UCAL_HOUR
, skeleton
, bestSkeleton
, differenceInfo
);
1037 setIntervalPattern(UCAL_AM_PM
, skeleton
, bestSkeleton
, differenceInfo
);
1045 DateIntervalFormat::setFallbackPattern(UCalendarDateFields field
,
1046 const UnicodeString
& skeleton
,
1047 UErrorCode
& status
) {
1048 if ( U_FAILURE(status
) ) {
1051 UnicodeString pattern
= fDtpng
->getBestPattern(skeleton
, status
);
1052 if ( U_FAILURE(status
) ) {
1055 setPatternInfo(field
, NULL
, &pattern
, fInfo
->getDefaultOrder());
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
,
1074 if ( U_FAILURE(status
) ) {
1077 PatternInfo
& ptn
= fIntervalPatterns
[itvPtnIndex
];
1079 ptn
.firstPart
= *firstPart
;
1082 ptn
.secondPart
= *secondPart
;
1084 ptn
.laterDateFirst
= laterDateFirst
;
1088 DateIntervalFormat::setIntervalPattern(UCalendarDateFields field
,
1089 const UnicodeString
& intervalPattern
) {
1090 UBool order
= fInfo
->getDefaultOrder();
1091 setIntervalPattern(field
, intervalPattern
, order
);
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
) ) {
1107 intervalPattern
.extract(prefixLength
,
1108 intervalPattern
.length() - prefixLength
,
1110 pattern
= &realPattern
;
1111 } else if ( intervalPattern
.startsWith(gEarlierFirstPrefix
,
1112 earliestFirstLength
) ) {
1114 intervalPattern
.extract(earliestFirstLength
,
1115 intervalPattern
.length() - earliestFirstLength
,
1117 pattern
= &realPattern
;
1120 int32_t splitPoint
= splitPatternInto2Part(*pattern
);
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
);
1128 setPatternInfo(field
, &firstPart
, &secondPart
, order
);
1135 * Generate interval pattern from existing resource
1137 * It not only save the interval patterns,
1138 * but also return the extended skeleton and its best match skeleton.
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
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.
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() ) {
1171 if ( SimpleDateFormat::isFieldUnitIgnored(*bestSkeleton
, field
) ) {
1172 // do nothing, format will handle it
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
);
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
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
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
;
1214 if ( !pattern
.isEmpty() ) {
1215 if ( differenceInfo
!= 0 ) {
1216 UnicodeString adjustIntervalPattern
;
1217 adjustFieldWidth(*skeleton
, *bestSkeleton
, pattern
, differenceInfo
,
1218 adjustIntervalPattern
);
1219 setIntervalPattern(field
, adjustIntervalPattern
);
1221 setIntervalPattern(field
, pattern
);
1223 if ( extendedSkeleton
&& !extendedSkeleton
->isEmpty() ) {
1233 DateIntervalFormat::splitPatternInto2Part(const UnicodeString
& intervalPattern
) {
1234 UBool inQuote
= false;
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
1242 UBool patternRepeated
[] =
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
1254 int8_t PATTERN_CHAR_BASE
= 0x41;
1256 /* loop through the pattern string character by character looking for
1257 * the first repeated pattern letter, which breaks the interval pattern
1261 UBool foundRepetition
= false;
1262 for (i
= 0; i
< intervalPattern
.length(); ++i
) {
1263 UChar ch
= intervalPattern
.charAt(i
);
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
;
1271 foundRepetition
= true;
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) == '\'') {
1283 inQuote
= ! inQuote
;
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
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
) {
1308 DateIntervalFormat::fallbackFormat(Calendar
& fromCalendar
,
1309 Calendar
& toCalendar
,
1310 UnicodeString
& appendTo
,
1312 UErrorCode
& status
) const {
1313 if ( U_FAILURE(status
) ) {
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
);
1328 UnicodeString fallback
;
1329 MessageFormat::format(fallbackPattern
, fmtArray
, 2, fallback
, status
);
1330 if ( U_SUCCESS(status
) ) {
1331 appendTo
.append(fallback
);
1340 DateIntervalFormat::fieldExistsInSkeleton(UCalendarDateFields field
,
1341 const UnicodeString
& skeleton
)
1343 const UChar fieldChar
= fgCalendarFieldToPatternLetter
[field
];
1344 return ( (skeleton
.indexOf(fieldChar
) == -1)?FALSE
:TRUE
) ;
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
[] =
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
1368 int32_t bestMatchSkeletonFieldWidth
[] =
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
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 */));
1387 UBool inQuote
= false;
1391 const int8_t PATTERN_CHAR_BASE
= 0x41;
1393 // loop through the pattern string character by character
1394 int32_t adjustedPtnLength
= adjustedPtn
.length();
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
;
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
;
1412 for ( j
= 0; j
< count
; ++j
) {
1413 adjustedPtn
.insert(i
, prevCh
);
1416 adjustedPtnLength
+= count
;
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) == '\'') {
1426 inQuote
= ! inQuote
;
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
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
;
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
;
1451 for ( j
= 0; j
< count
; ++j
) {
1452 adjustedPtn
.append(prevCh
);
1461 DateIntervalFormat::concatSingleDate2TimeInterval(const UChar
* format
,
1463 const UnicodeString
& datePattern
,
1464 UCalendarDateFields field
,
1465 UErrorCode
& status
) {
1466 // following should not set wrong status
1467 int32_t itvPtnIndex
= DateIntervalInfo::calendarFieldToIntervalIndex(field
,
1469 if ( U_FAILURE(status
) ) {
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
) ) {
1487 setIntervalPattern(field
, combinedPattern
, timeItvPtnInfo
.laterDateFirst
);
1490 // it should not happen if the interval format defined is valid
1496 DateIntervalFormat::fgCalendarFieldToPatternLetter
[] =
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
,