1 /*******************************************************************************
2 * Copyright (C) 2008-2015, International Business Machines Corporation and
3 * others. All Rights Reserved.
4 *******************************************************************************
8 *******************************************************************************
11 #include "utypeinfo.h" // 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()
124 fDateTimeFormat(NULL
),
125 fMinimizeType(UDTITVFMT_MINIMIZE_NONE
)
129 DateIntervalFormat::DateIntervalFormat(const DateIntervalFormat
& itvfmt
)
138 fDateTimeFormat(NULL
),
139 fMinimizeType(UDTITVFMT_MINIMIZE_NONE
) {
145 DateIntervalFormat::operator=(const DateIntervalFormat
& itvfmt
) {
146 if ( this != &itvfmt
) {
149 delete fFromCalendar
;
154 delete fDateTimeFormat
;
155 if ( itvfmt
.fDateFormat
) {
156 fDateFormat
= (SimpleDateFormat
*)itvfmt
.fDateFormat
->clone();
160 if ( itvfmt
.fInfo
) {
161 fInfo
= itvfmt
.fInfo
->clone();
165 if ( itvfmt
.fFromCalendar
) {
166 fFromCalendar
= itvfmt
.fFromCalendar
->clone();
168 fFromCalendar
= NULL
;
170 if ( itvfmt
.fToCalendar
) {
171 fToCalendar
= itvfmt
.fToCalendar
->clone();
175 fSkeleton
= itvfmt
.fSkeleton
;
177 for ( i
= 0; i
< DateIntervalInfo::kIPI_MAX_INDEX
; ++i
) {
178 fIntervalPatterns
[i
] = itvfmt
.fIntervalPatterns
[i
];
181 fDtpng
= itvfmt
.fDtpng
->clone();
185 fDatePattern
= (itvfmt
.fDatePattern
)? (UnicodeString
*)itvfmt
.fDatePattern
->clone(): NULL
;
186 fTimePattern
= (itvfmt
.fTimePattern
)? (UnicodeString
*)itvfmt
.fTimePattern
->clone(): NULL
;
187 fDateTimeFormat
= (itvfmt
.fDateTimeFormat
)? (UnicodeString
*)itvfmt
.fDateTimeFormat
->clone(): NULL
;
193 DateIntervalFormat::~DateIntervalFormat() {
196 delete fFromCalendar
;
201 delete fDateTimeFormat
;
206 DateIntervalFormat::clone(void) const {
207 return new DateIntervalFormat(*this);
212 DateIntervalFormat::operator==(const Format
& other
) const {
213 if (typeid(*this) == typeid(other
)) {
214 const DateIntervalFormat
* fmt
= (DateIntervalFormat
*)&other
;
215 #ifdef DTITVFMT_DEBUG
217 equal
= (this == fmt
);
219 equal
= (*fInfo
== *fmt
->fInfo
);
220 equal
= (*fDateFormat
== *fmt
->fDateFormat
);
221 equal
= fFromCalendar
->isEquivalentTo(*fmt
->fFromCalendar
) ;
222 equal
= fToCalendar
->isEquivalentTo(*fmt
->fToCalendar
) ;
223 equal
= (fSkeleton
== fmt
->fSkeleton
);
224 equal
= ((fDatePattern
== NULL
&& fmt
->fDatePattern
== NULL
) || (fDatePattern
&& fmt
->fDatePattern
&& *fDatePattern
== *fmt
->fDatePattern
));
225 equal
= ((fTimePattern
== NULL
&& fmt
->fTimePattern
== NULL
) || (fTimePattern
&& fmt
->fTimePattern
&& *fTimePattern
== *fmt
->fTimePattern
));
226 equal
= ((fDateTimeFormat
== NULL
&& fmt
->fDateTimeFormat
== NULL
) || (fDateTimeFormat
&& fmt
->fDateTimeFormat
&& *fDateTimeFormat
== *fmt
->fDateTimeFormat
));
229 res
= ( this == fmt
) ||
230 ( Format::operator==(other
) &&
232 ( *fInfo
== *fmt
->fInfo
) &&
234 ( *fDateFormat
== *fmt
->fDateFormat
) &&
236 fFromCalendar
->isEquivalentTo(*fmt
->fFromCalendar
) &&
238 fToCalendar
->isEquivalentTo(*fmt
->fToCalendar
) &&
239 fSkeleton
== fmt
->fSkeleton
&&
240 ((fDatePattern
== NULL
&& fmt
->fDatePattern
== NULL
) || (fDatePattern
&& fmt
->fDatePattern
&& *fDatePattern
== *fmt
->fDatePattern
)) &&
241 ((fTimePattern
== NULL
&& fmt
->fTimePattern
== NULL
) || (fTimePattern
&& fmt
->fTimePattern
&& *fTimePattern
== *fmt
->fTimePattern
)) &&
242 ((fDateTimeFormat
== NULL
&& fmt
->fDateTimeFormat
== NULL
) || (fDateTimeFormat
&& fmt
->fDateTimeFormat
&& *fDateTimeFormat
== *fmt
->fDateTimeFormat
)) &&
244 (*fDtpng
== *fmt
->fDtpng
) );
246 for (i
= 0; i
< DateIntervalInfo::kIPI_MAX_INDEX
&& res
== TRUE
; ++i
) {
247 res
= ( fIntervalPatterns
[i
].firstPart
==
248 fmt
->fIntervalPatterns
[i
].firstPart
) &&
249 ( fIntervalPatterns
[i
].secondPart
==
250 fmt
->fIntervalPatterns
[i
].secondPart
) &&
251 ( fIntervalPatterns
[i
].laterDateFirst
==
252 fmt
->fIntervalPatterns
[i
].laterDateFirst
) ;
262 DateIntervalFormat::format(const Formattable
& obj
,
263 UnicodeString
& appendTo
,
264 FieldPosition
& fieldPosition
,
265 UErrorCode
& status
) const {
266 if ( U_FAILURE(status
) ) {
270 if ( obj
.getType() == Formattable::kObject
) {
271 const UObject
* formatObj
= obj
.getObject();
272 const DateInterval
* interval
= dynamic_cast<const DateInterval
*>(formatObj
);
273 if (interval
!= NULL
){
274 return format(interval
, appendTo
, fieldPosition
, status
);
277 status
= U_ILLEGAL_ARGUMENT_ERROR
;
283 DateIntervalFormat::format(const DateInterval
* dtInterval
,
284 UnicodeString
& appendTo
,
285 FieldPosition
& fieldPosition
,
286 UErrorCode
& status
) const {
287 if ( U_FAILURE(status
) ) {
291 if ( fFromCalendar
!= NULL
&& fToCalendar
!= NULL
&&
292 fDateFormat
!= NULL
&& fInfo
!= NULL
) {
293 fFromCalendar
->setTime(dtInterval
->getFromDate(), status
);
294 fToCalendar
->setTime(dtInterval
->getToDate(), status
);
295 if ( U_SUCCESS(status
) ) {
296 return format(*fFromCalendar
, *fToCalendar
, appendTo
,fieldPosition
, status
);
304 DateIntervalFormat::format(Calendar
& fromCalendar
,
305 Calendar
& toCalendar
,
306 UnicodeString
& appendTo
,
308 UErrorCode
& status
) const {
309 if ( U_FAILURE(status
) ) {
313 // not support different calendar types and time zones
314 //if ( fromCalendar.getType() != toCalendar.getType() ) {
315 if ( !fromCalendar
.isEquivalentTo(toCalendar
) ) {
316 status
= U_ILLEGAL_ARGUMENT_ERROR
;
320 // First, find the largest different calendar field.
321 UCalendarDateFields field
= UCAL_FIELD_COUNT
;
323 if ( fromCalendar
.get(UCAL_ERA
,status
) != toCalendar
.get(UCAL_ERA
,status
)) {
325 } else if ( fromCalendar
.get(UCAL_YEAR
, status
) !=
326 toCalendar
.get(UCAL_YEAR
, status
) ) {
328 } else if ( fromCalendar
.get(UCAL_MONTH
, status
) !=
329 toCalendar
.get(UCAL_MONTH
, status
) ) {
331 UChar patternDay
= 0x0064; // d
332 if (fMinimizeType
== UDTITVFMT_MINIMIZE_ADJACENT_MONTHS
&& fSkeleton
.indexOf(patternDay
) >= 0) {
333 UDate fromDate
= fromCalendar
.getTime(status
);
334 UDate toDate
= toCalendar
.getTime(status
);
335 int32_t fromDay
= fromCalendar
.get(UCAL_DATE
, status
);
336 int32_t toDay
= toCalendar
.get(UCAL_DATE
, status
);
337 fromCalendar
.add(UCAL_MONTH
, 1, status
);
338 if ( fromDate
< toDate
&& fromCalendar
.getTime(status
) > toDate
&& fromDay
> toDay
) {
341 fromCalendar
.setTime(fromDate
, status
);
343 } else if ( fromCalendar
.get(UCAL_DATE
, status
) !=
344 toCalendar
.get(UCAL_DATE
, status
) ) {
346 } else if ( fromCalendar
.get(UCAL_AM_PM
, status
) !=
347 toCalendar
.get(UCAL_AM_PM
, status
) ) {
349 } else if ( fromCalendar
.get(UCAL_HOUR
, status
) !=
350 toCalendar
.get(UCAL_HOUR
, status
) ) {
352 } else if ( fromCalendar
.get(UCAL_MINUTE
, status
) !=
353 toCalendar
.get(UCAL_MINUTE
, status
) ) {
355 } else if ( fromCalendar
.get(UCAL_SECOND
, status
) !=
356 toCalendar
.get(UCAL_SECOND
, status
) ) {
360 if ( U_FAILURE(status
) ) {
363 if ( field
== UCAL_FIELD_COUNT
) {
364 /* ignore the millisecond etc. small fields' difference.
365 * use single date when all the above are the same.
367 return fDateFormat
->format(fromCalendar
, appendTo
, pos
);
369 UBool fromToOnSameDay
= (field
==UCAL_AM_PM
|| field
==UCAL_HOUR
|| field
==UCAL_MINUTE
|| field
==UCAL_SECOND
);
371 // following call should not set wrong status,
372 // all the pass-in fields are valid till here
373 int32_t itvPtnIndex
= DateIntervalInfo::calendarFieldToIntervalIndex(field
,
375 const PatternInfo
& intervalPattern
= fIntervalPatterns
[itvPtnIndex
];
377 if ( intervalPattern
.firstPart
.isEmpty() &&
378 intervalPattern
.secondPart
.isEmpty() ) {
379 if ( fDateFormat
->isFieldUnitIgnored(field
) ) {
380 /* the largest different calendar field is small than
381 * the smallest calendar field in pattern,
382 * return single date format.
384 return fDateFormat
->format(fromCalendar
, appendTo
, pos
);
386 return fallbackFormat(fromCalendar
, toCalendar
, fromToOnSameDay
, appendTo
, pos
, status
);
388 // If the first part in interval pattern is empty,
389 // the 2nd part of it saves the full-pattern used in fall-back.
390 // For a 'real' interval pattern, the first part will never be empty.
391 if ( intervalPattern
.firstPart
.isEmpty() ) {
393 UnicodeString originalPattern
;
394 fDateFormat
->toPattern(originalPattern
);
395 fDateFormat
->applyPattern(intervalPattern
.secondPart
);
396 appendTo
= fallbackFormat(fromCalendar
, toCalendar
, fromToOnSameDay
, appendTo
, pos
, status
);
397 fDateFormat
->applyPattern(originalPattern
);
402 if ( intervalPattern
.laterDateFirst
) {
403 firstCal
= &toCalendar
;
404 secondCal
= &fromCalendar
;
406 firstCal
= &fromCalendar
;
407 secondCal
= &toCalendar
;
409 // break the interval pattern into 2 parts,
410 // first part should not be empty,
411 UnicodeString originalPattern
;
412 fDateFormat
->toPattern(originalPattern
);
413 fDateFormat
->applyPattern(intervalPattern
.firstPart
);
414 fDateFormat
->format(*firstCal
, appendTo
, pos
);
415 if ( !intervalPattern
.secondPart
.isEmpty() ) {
416 fDateFormat
->applyPattern(intervalPattern
.secondPart
);
417 FieldPosition otherPos
;
418 otherPos
.setField(pos
.getField());
419 fDateFormat
->format(*secondCal
, appendTo
, otherPos
);
420 if (pos
.getEndIndex() == 0 && otherPos
.getEndIndex() > 0) {
424 fDateFormat
->applyPattern(originalPattern
);
431 DateIntervalFormat::parseObject(const UnicodeString
& /* source */,
432 Formattable
& /* result */,
433 ParsePosition
& /* parse_pos */) const {
434 // parseObject(const UnicodeString&, Formattable&, UErrorCode&) const
435 // will set status as U_INVALID_FORMAT_ERROR if
436 // parse_pos is still 0
442 const DateIntervalInfo
*
443 DateIntervalFormat::getDateIntervalInfo() const {
449 DateIntervalFormat::setDateIntervalInfo(const DateIntervalInfo
& newItvPattern
,
450 UErrorCode
& status
) {
452 fInfo
= new DateIntervalInfo(newItvPattern
);
454 initializePattern(status
);
461 DateIntervalFormat::getDateFormat() const {
467 DateIntervalFormat::adoptTimeZone(TimeZone
* zone
)
469 if (fDateFormat
!= NULL
) {
470 fDateFormat
->adoptTimeZone(zone
);
472 // The fDateFormat has the master calendar for the DateIntervalFormat and has
473 // ownership of any adopted TimeZone; fFromCalendar and fToCalendar are internal
474 // work clones of that calendar (and should not also be given ownership of the
475 // adopted TimeZone).
477 fFromCalendar
->setTimeZone(*zone
);
480 fToCalendar
->setTimeZone(*zone
);
485 DateIntervalFormat::setTimeZone(const TimeZone
& zone
)
487 if (fDateFormat
!= NULL
) {
488 fDateFormat
->setTimeZone(zone
);
490 // The fDateFormat has the master calendar for the DateIntervalFormat;
491 // fFromCalendar and fToCalendar are internal work clones of that calendar.
493 fFromCalendar
->setTimeZone(zone
);
496 fToCalendar
->setTimeZone(zone
);
501 DateIntervalFormat::getTimeZone() const
503 if (fDateFormat
!= NULL
) {
504 return fDateFormat
->getTimeZone();
506 // If fDateFormat is NULL (unexpected), create default timezone.
507 return *(TimeZone::createDefault());
511 DateIntervalFormat::setAttribute(UDateIntervalFormatAttribute attr
,
512 UDateIntervalFormatAttributeValue value
,
515 if ( U_FAILURE(status
) ) {
518 if (attr
== UDTITVFMT_MINIMIZE_TYPE
) {
519 fMinimizeType
= value
;
521 status
= U_ILLEGAL_ARGUMENT_ERROR
;
525 DateIntervalFormat::DateIntervalFormat(const Locale
& locale
,
526 DateIntervalInfo
* dtItvInfo
,
527 const UnicodeString
* skeleton
,
536 fDateTimeFormat(NULL
),
537 fMinimizeType(UDTITVFMT_MINIMIZE_NONE
)
539 if ( U_FAILURE(status
) ) {
543 fDtpng
= DateTimePatternGenerator::createInstance(locale
, status
);
544 SimpleDateFormat
* dtfmt
= createSDFPatternInstance(*skeleton
, locale
,
546 if ( U_FAILURE(status
) ) {
552 if ( dtfmt
== NULL
|| dtItvInfo
== NULL
|| fDtpng
== NULL
) {
553 status
= U_MEMORY_ALLOCATION_ERROR
;
554 // safe to delete NULL
561 fSkeleton
= *skeleton
;
565 if ( dtfmt
->getCalendar() ) {
566 fFromCalendar
= dtfmt
->getCalendar()->clone();
567 fToCalendar
= dtfmt
->getCalendar()->clone();
569 fFromCalendar
= NULL
;
572 initializePattern(status
);
576 SimpleDateFormat
* U_EXPORT2
577 DateIntervalFormat::createSDFPatternInstance(const UnicodeString
& skeleton
,
578 const Locale
& locale
,
579 DateTimePatternGenerator
* dtpng
,
582 DateFormat
*df
= DateFormat::internalCreateInstanceForSkeleton(
583 skeleton
, locale
, *dtpng
, status
);
584 return static_cast<SimpleDateFormat
*>(df
);
588 DateIntervalFormat
* U_EXPORT2
589 DateIntervalFormat::create(const Locale
& locale
,
590 DateIntervalInfo
* dtitvinf
,
591 const UnicodeString
* skeleton
,
592 UErrorCode
& status
) {
593 DateIntervalFormat
* f
= new DateIntervalFormat(locale
, dtitvinf
,
596 status
= U_MEMORY_ALLOCATION_ERROR
;
598 } else if ( U_FAILURE(status
) ) {
599 // safe to delete f, although nothing acutally is saved
609 * Initialize interval patterns locale to this formatter
611 * This code is a bit complicated since
612 * 1. the interval patterns saved in resource bundle files are interval
613 * patterns based on date or time only.
614 * It does not have interval patterns based on both date and time.
615 * Interval patterns on both date and time are algorithm generated.
617 * For example, it has interval patterns on skeleton "dMy" and "hm",
618 * but it does not have interval patterns on skeleton "dMyhm".
620 * The rule to genearte interval patterns for both date and time skeleton are
621 * 1) when the year, month, or day differs, concatenate the two original
622 * expressions with a separator between,
623 * For example, interval pattern from "Jan 10, 2007 10:10 am"
624 * to "Jan 11, 2007 10:10am" is
625 * "Jan 10, 2007 10:10 am - Jan 11, 2007 10:10am"
627 * 2) otherwise, present the date followed by the range expression
629 * For example, interval pattern from "Jan 10, 2007 10:10 am"
630 * to "Jan 10, 2007 11:10am" is
631 * "Jan 10, 2007 10:10 am - 11:10am"
633 * 2. even a pattern does not request a certion calendar field,
634 * the interval pattern needs to include such field if such fields are
635 * different between 2 dates.
636 * For example, a pattern/skeleton is "hm", but the interval pattern
637 * includes year, month, and date when year, month, and date differs.
639 * @param status output param set to success/failure code on exit
643 DateIntervalFormat::initializePattern(UErrorCode
& status
) {
644 if ( U_FAILURE(status
) ) {
647 const Locale
& locale
= fDateFormat
->getSmpFmtLocale();
648 if ( fSkeleton
.isEmpty() ) {
649 UnicodeString fullPattern
;
650 fDateFormat
->toPattern(fullPattern
);
651 #ifdef DTITVFMT_DEBUG
655 fSkeleton
.extract(0, fSkeleton
.length(), result
, "UTF-8");
656 sprintf(mesg
, "in getBestSkeleton: fSkeleton: %s; \n", result
);
659 // fSkeleton is already set by createDateIntervalInstance()
660 // or by createInstance(UnicodeString skeleton, .... )
661 fSkeleton
= fDtpng
->getSkeleton(fullPattern
, status
);
662 if ( U_FAILURE(status
) ) {
667 // initialize the fIntervalPattern ordering
669 for ( i
= 0; i
< DateIntervalInfo::kIPI_MAX_INDEX
; ++i
) {
670 fIntervalPatterns
[i
].laterDateFirst
= fInfo
->getDefaultOrder();
673 /* Check whether the skeleton is a combination of date and time.
674 * For the complication reason 1 explained above.
676 UnicodeString dateSkeleton
;
677 UnicodeString timeSkeleton
;
678 UnicodeString normalizedTimeSkeleton
;
679 UnicodeString normalizedDateSkeleton
;
682 /* the difference between time skeleton and normalizedTimeSkeleton are:
683 * 1. (Formerly, normalized time skeleton folded 'H' to 'h'; no longer true)
684 * 2. 'a' is omitted in normalized time skeleton.
685 * 3. there is only one appearance for 'h' or 'H', 'm','v', 'z' in normalized
688 * The difference between date skeleton and normalizedDateSkeleton are:
689 * 1. both 'y' and 'd' appear only once in normalizeDateSkeleton
690 * 2. 'E' and 'EE' are normalized into 'EEE'
691 * 3. 'MM' is normalized into 'M'
693 getDateTimeSkeleton(fSkeleton
, dateSkeleton
, normalizedDateSkeleton
,
694 timeSkeleton
, normalizedTimeSkeleton
);
696 #ifdef DTITVFMT_DEBUG
700 fSkeleton
.extract(0, fSkeleton
.length(), result
, "UTF-8");
701 sprintf(mesg
, "in getBestSkeleton: fSkeleton: %s; \n", result
);
705 // move this up here since we need it for fallbacks
706 if ( timeSkeleton
.length() > 0 && dateSkeleton
.length() > 0 ) {
707 // Need the Date/Time pattern for concatnation the date with
708 // the time interval.
709 // The date/time pattern ( such as {0} {1} ) is saved in
710 // calendar, that is why need to get the CalendarData here.
711 CalendarData
* calData
= new CalendarData(locale
, NULL
, status
);
712 if ( U_FAILURE(status
) ) {
716 if ( calData
== NULL
) {
717 status
= U_MEMORY_ALLOCATION_ERROR
;
721 const UResourceBundle
* dateTimePatternsRes
= calData
->getByKey(
722 gDateTimePatternsTag
, status
);
723 int32_t dateTimeFormatLength
;
724 const UChar
* dateTimeFormat
= ures_getStringByIndex(
726 (int32_t)DateFormat::kDateTime
,
727 &dateTimeFormatLength
, &status
);
728 if ( U_SUCCESS(status
) && dateTimeFormatLength
>= 3 ) {
729 fDateTimeFormat
= new UnicodeString(dateTimeFormat
, dateTimeFormatLength
);
734 UBool found
= setSeparateDateTimePtn(normalizedDateSkeleton
,
735 normalizedTimeSkeleton
);
737 // for skeletons with seconds, found is false and we enter this block
738 if ( found
== false ) {
740 // TODO: if user asks "m"(minute), but "d"(day) differ
741 if ( timeSkeleton
.length() != 0 ) {
742 if ( dateSkeleton
.length() == 0 ) {
744 timeSkeleton
.insert(0, gDateFormatSkeleton
[DateFormat::kShort
], -1);
745 UnicodeString pattern
= fDtpng
->getBestPattern(timeSkeleton
, status
);
746 if ( U_FAILURE(status
) ) {
749 // for fall back interval patterns,
750 // the first part of the pattern is empty,
751 // the second part of the pattern is the full-pattern
752 // should be used in fall-back.
753 setPatternInfo(UCAL_DATE
, NULL
, &pattern
, fInfo
->getDefaultOrder());
754 setPatternInfo(UCAL_MONTH
, NULL
, &pattern
, fInfo
->getDefaultOrder());
755 setPatternInfo(UCAL_YEAR
, NULL
, &pattern
, fInfo
->getDefaultOrder());
763 } // end of skeleton not found
764 // interval patterns for skeleton are found in resource
765 if ( timeSkeleton
.length() == 0 ) {
767 } else if ( dateSkeleton
.length() == 0 ) {
769 timeSkeleton
.insert(0, gDateFormatSkeleton
[DateFormat::kShort
], -1);
770 UnicodeString pattern
= fDtpng
->getBestPattern(timeSkeleton
, status
);
771 if ( U_FAILURE(status
) ) {
774 // for fall back interval patterns,
775 // the first part of the pattern is empty,
776 // the second part of the pattern is the full-pattern
777 // should be used in fall-back.
778 setPatternInfo(UCAL_DATE
, NULL
, &pattern
, fInfo
->getDefaultOrder());
779 setPatternInfo(UCAL_MONTH
, NULL
, &pattern
, fInfo
->getDefaultOrder());
780 setPatternInfo(UCAL_YEAR
, NULL
, &pattern
, fInfo
->getDefaultOrder());
783 * 1) when the year, month, or day differs,
784 * concatenate the two original expressions with a separator between,
785 * 2) otherwise, present the date followed by the
786 * range expression for the time.
789 * 1) when the year, month, or day differs,
790 * concatenate the two original expressions with a separator between,
792 // if field exists, use fall back
793 UnicodeString skeleton
= fSkeleton
;
794 if ( !fieldExistsInSkeleton(UCAL_DATE
, dateSkeleton
) ) {
795 // prefix skeleton with 'd'
796 skeleton
.insert(0, LOW_D
);
797 setFallbackPattern(UCAL_DATE
, skeleton
, status
);
799 if ( !fieldExistsInSkeleton(UCAL_MONTH
, dateSkeleton
) ) {
800 // then prefix skeleton with 'M'
801 skeleton
.insert(0, CAP_M
);
802 setFallbackPattern(UCAL_MONTH
, skeleton
, status
);
804 if ( !fieldExistsInSkeleton(UCAL_YEAR
, dateSkeleton
) ) {
805 // then prefix skeleton with 'y'
806 skeleton
.insert(0, LOW_Y
);
807 setFallbackPattern(UCAL_YEAR
, skeleton
, status
);
811 * 2) otherwise, present the date followed by the
812 * range expression for the time.
815 if ( fDateTimeFormat
== 0 ) {
816 // earlier failure getting dateTimeFormat
820 UnicodeString datePattern
= fDtpng
->getBestPattern(dateSkeleton
, status
);
822 concatSingleDate2TimeInterval(*fDateTimeFormat
, datePattern
, UCAL_AM_PM
, status
);
823 concatSingleDate2TimeInterval(*fDateTimeFormat
, datePattern
, UCAL_HOUR
, status
);
824 concatSingleDate2TimeInterval(*fDateTimeFormat
, datePattern
, UCAL_MINUTE
, status
);
831 DateIntervalFormat::getDateTimeSkeleton(const UnicodeString
& skeleton
,
832 UnicodeString
& dateSkeleton
,
833 UnicodeString
& normalizedDateSkeleton
,
834 UnicodeString
& timeSkeleton
,
835 UnicodeString
& normalizedTimeSkeleton
) {
836 // dateSkeleton follows the sequence of y*M*E*d*
837 // timeSkeleton follows the sequence of hm*[v|z]?
849 for (i
= 0; i
< skeleton
.length(); ++i
) {
850 UChar ch
= skeleton
[i
];
853 dateSkeleton
.append(ch
);
857 dateSkeleton
.append(ch
);
861 dateSkeleton
.append(ch
);
865 dateSkeleton
.append(ch
);
884 normalizedDateSkeleton
.append(ch
);
885 dateSkeleton
.append(ch
);
888 // 'a' is implicitly handled
889 timeSkeleton
.append(ch
);
892 timeSkeleton
.append(ch
);
896 timeSkeleton
.append(ch
);
900 timeSkeleton
.append(ch
);
905 timeSkeleton
.append(ch
);
909 timeSkeleton
.append(ch
);
919 timeSkeleton
.append(ch
);
920 normalizedTimeSkeleton
.append(ch
);
925 /* generate normalized form for date*/
927 for (i
= 0; i
< yCount
; ++i
) {
928 normalizedDateSkeleton
.append(LOW_Y
);
933 normalizedDateSkeleton
.append(CAP_M
);
936 for ( i
= 0; i
< MCount
&& i
< MAX_M_COUNT
; ++i
) {
937 normalizedDateSkeleton
.append(CAP_M
);
943 normalizedDateSkeleton
.append(CAP_E
);
946 for ( i
= 0; i
< ECount
&& i
< MAX_E_COUNT
; ++i
) {
947 normalizedDateSkeleton
.append(CAP_E
);
952 normalizedDateSkeleton
.append(LOW_D
);
955 /* generate normalized form for time */
957 normalizedTimeSkeleton
.append(CAP_H
);
959 else if ( hCount
!= 0 ) {
960 normalizedTimeSkeleton
.append(LOW_H
);
963 normalizedTimeSkeleton
.append(LOW_M
);
966 normalizedTimeSkeleton
.append(LOW_Z
);
969 normalizedTimeSkeleton
.append(LOW_V
);
975 * Generate date or time interval pattern from resource,
976 * and set them into the interval pattern locale to this formatter.
978 * It needs to handle the following:
979 * 1. need to adjust field width.
980 * For example, the interval patterns saved in DateIntervalInfo
981 * includes "dMMMy", but not "dMMMMy".
982 * Need to get interval patterns for dMMMMy from dMMMy.
983 * Another example, the interval patterns saved in DateIntervalInfo
984 * includes "hmv", but not "hmz".
985 * Need to get interval patterns for "hmz' from 'hmv'
987 * 2. there might be no pattern for 'y' differ for skeleton "Md",
988 * in order to get interval patterns for 'y' differ,
989 * need to look for it from skeleton 'yMd'
991 * @param dateSkeleton normalized date skeleton
992 * @param timeSkeleton normalized time skeleton
993 * @return whether the resource is found for the skeleton.
994 * TRUE if interval pattern found for the skeleton,
999 DateIntervalFormat::setSeparateDateTimePtn(
1000 const UnicodeString
& dateSkeleton
,
1001 const UnicodeString
& timeSkeleton
) {
1002 const UnicodeString
* skeleton
;
1003 // if both date and time skeleton present,
1004 // the final interval pattern might include time interval patterns
1005 // ( when, am_pm, hour, minute differ ),
1006 // but not date interval patterns ( when year, month, day differ ).
1007 // For year/month/day differ, it falls back to fall-back pattern.
1008 if ( timeSkeleton
.length() != 0 ) {
1009 skeleton
= &timeSkeleton
;
1011 skeleton
= &dateSkeleton
;
1014 /* interval patterns for skeleton "dMMMy" (but not "dMMMMy")
1015 * are defined in resource,
1016 * interval patterns for skeleton "dMMMMy" are calculated by
1017 * 1. get the best match skeleton for "dMMMMy", which is "dMMMy"
1018 * 2. get the interval patterns for "dMMMy",
1019 * 3. extend "MMM" to "MMMM" in above interval patterns for "dMMMMy"
1020 * getBestSkeleton() is step 1.
1022 // best skeleton, and the difference information
1023 int8_t differenceInfo
= 0;
1024 const UnicodeString
* bestSkeleton
= fInfo
->getBestSkeleton(*skeleton
,
1026 /* best skeleton could be NULL.
1027 For example: in "ca" resource file,
1028 interval format is defined as following
1030 fallback{"{0} - {1}"}
1032 there is no skeletons/interval patterns defined,
1033 and the best skeleton match could be NULL
1035 if ( bestSkeleton
== NULL
) {
1039 // Set patterns for fallback use, need to do this
1040 // before returning if differenceInfo == -1
1042 if ( dateSkeleton
.length() != 0 && fDtpng
!= NULL
) {
1043 status
= U_ZERO_ERROR
;
1044 fDatePattern
= new UnicodeString(fDtpng
->getBestPattern(dateSkeleton
, status
));
1046 if ( timeSkeleton
.length() != 0 && fDtpng
!= NULL
) {
1047 status
= U_ZERO_ERROR
;
1048 fTimePattern
= new UnicodeString(fDtpng
->getBestPattern(timeSkeleton
, status
));
1052 // 0 means the best matched skeleton is the same as input skeleton
1053 // 1 means the fields are the same, but field width are different
1054 // 2 means the only difference between fields are v/z,
1055 // -1 means there are other fields difference
1056 // (this will happen, for instance, if the supplied skeleton has seconds,
1057 // but no skeletons in the intervalFormats data do)
1058 if ( differenceInfo
== -1 ) {
1059 // skeleton has different fields, not only v/z difference
1063 if ( timeSkeleton
.length() == 0 ) {
1064 UnicodeString extendedSkeleton
;
1065 UnicodeString extendedBestSkeleton
;
1066 // only has date skeleton
1067 setIntervalPattern(UCAL_DATE
, skeleton
, bestSkeleton
, differenceInfo
,
1068 &extendedSkeleton
, &extendedBestSkeleton
);
1070 UBool extended
= setIntervalPattern(UCAL_MONTH
, skeleton
, bestSkeleton
,
1072 &extendedSkeleton
, &extendedBestSkeleton
);
1075 bestSkeleton
= &extendedBestSkeleton
;
1076 skeleton
= &extendedSkeleton
;
1078 setIntervalPattern(UCAL_YEAR
, skeleton
, bestSkeleton
, differenceInfo
,
1079 &extendedSkeleton
, &extendedBestSkeleton
);
1081 setIntervalPattern(UCAL_MINUTE
, skeleton
, bestSkeleton
, differenceInfo
);
1082 setIntervalPattern(UCAL_HOUR
, skeleton
, bestSkeleton
, differenceInfo
);
1083 setIntervalPattern(UCAL_AM_PM
, skeleton
, bestSkeleton
, differenceInfo
);
1091 DateIntervalFormat::setFallbackPattern(UCalendarDateFields field
,
1092 const UnicodeString
& skeleton
,
1093 UErrorCode
& status
) {
1094 if ( U_FAILURE(status
) ) {
1097 UnicodeString pattern
= fDtpng
->getBestPattern(skeleton
, status
);
1098 if ( U_FAILURE(status
) ) {
1101 setPatternInfo(field
, NULL
, &pattern
, fInfo
->getDefaultOrder());
1108 DateIntervalFormat::setPatternInfo(UCalendarDateFields field
,
1109 const UnicodeString
* firstPart
,
1110 const UnicodeString
* secondPart
,
1111 UBool laterDateFirst
) {
1112 // for fall back interval patterns,
1113 // the first part of the pattern is empty,
1114 // the second part of the pattern is the full-pattern
1115 // should be used in fall-back.
1116 UErrorCode status
= U_ZERO_ERROR
;
1117 // following should not set any wrong status.
1118 int32_t itvPtnIndex
= DateIntervalInfo::calendarFieldToIntervalIndex(field
,
1120 if ( U_FAILURE(status
) ) {
1123 PatternInfo
& ptn
= fIntervalPatterns
[itvPtnIndex
];
1125 ptn
.firstPart
= *firstPart
;
1128 ptn
.secondPart
= *secondPart
;
1130 ptn
.laterDateFirst
= laterDateFirst
;
1134 DateIntervalFormat::setIntervalPattern(UCalendarDateFields field
,
1135 const UnicodeString
& intervalPattern
) {
1136 UBool order
= fInfo
->getDefaultOrder();
1137 setIntervalPattern(field
, intervalPattern
, order
);
1142 DateIntervalFormat::setIntervalPattern(UCalendarDateFields field
,
1143 const UnicodeString
& intervalPattern
,
1144 UBool laterDateFirst
) {
1145 const UnicodeString
* pattern
= &intervalPattern
;
1146 UBool order
= laterDateFirst
;
1147 // check for "latestFirst:" or "earliestFirst:" prefix
1148 int8_t prefixLength
= sizeof(gLaterFirstPrefix
)/sizeof(gLaterFirstPrefix
[0]);
1149 int8_t earliestFirstLength
= sizeof(gEarlierFirstPrefix
)/sizeof(gEarlierFirstPrefix
[0]);
1150 UnicodeString realPattern
;
1151 if ( intervalPattern
.startsWith(gLaterFirstPrefix
, prefixLength
) ) {
1153 intervalPattern
.extract(prefixLength
,
1154 intervalPattern
.length() - prefixLength
,
1156 pattern
= &realPattern
;
1157 } else if ( intervalPattern
.startsWith(gEarlierFirstPrefix
,
1158 earliestFirstLength
) ) {
1160 intervalPattern
.extract(earliestFirstLength
,
1161 intervalPattern
.length() - earliestFirstLength
,
1163 pattern
= &realPattern
;
1166 int32_t splitPoint
= splitPatternInto2Part(*pattern
);
1168 UnicodeString firstPart
;
1169 UnicodeString secondPart
;
1170 pattern
->extract(0, splitPoint
, firstPart
);
1171 if ( splitPoint
< pattern
->length() ) {
1172 pattern
->extract(splitPoint
, pattern
->length()-splitPoint
, secondPart
);
1174 setPatternInfo(field
, &firstPart
, &secondPart
, order
);
1181 * Generate interval pattern from existing resource
1183 * It not only save the interval patterns,
1184 * but also return the extended skeleton and its best match skeleton.
1186 * @param field largest different calendar field
1187 * @param skeleton skeleton
1188 * @param bestSkeleton the best match skeleton which has interval pattern
1189 * defined in resource
1190 * @param differenceInfo the difference between skeleton and best skeleton
1191 * 0 means the best matched skeleton is the same as input skeleton
1192 * 1 means the fields are the same, but field width are different
1193 * 2 means the only difference between fields are v/z,
1194 * -1 means there are other fields difference
1196 * @param extendedSkeleton extended skeleton
1197 * @param extendedBestSkeleton extended best match skeleton
1198 * @return whether the interval pattern is found
1199 * through extending skeleton or not.
1200 * TRUE if interval pattern is found by
1201 * extending skeleton, FALSE otherwise.
1205 DateIntervalFormat::setIntervalPattern(UCalendarDateFields field
,
1206 const UnicodeString
* skeleton
,
1207 const UnicodeString
* bestSkeleton
,
1208 int8_t differenceInfo
,
1209 UnicodeString
* extendedSkeleton
,
1210 UnicodeString
* extendedBestSkeleton
) {
1211 UErrorCode status
= U_ZERO_ERROR
;
1212 // following getIntervalPattern() should not generate error status
1213 UnicodeString pattern
;
1214 fInfo
->getIntervalPattern(*bestSkeleton
, field
, pattern
, status
);
1215 if ( pattern
.isEmpty() ) {
1217 if ( SimpleDateFormat::isFieldUnitIgnored(*bestSkeleton
, field
) ) {
1218 // do nothing, format will handle it
1222 // for 24 hour system, interval patterns in resource file
1223 // might not include pattern when am_pm differ,
1224 // which should be the same as hour differ.
1225 // add it here for simplicity
1226 if ( field
== UCAL_AM_PM
) {
1227 fInfo
->getIntervalPattern(*bestSkeleton
, UCAL_HOUR
, pattern
,status
);
1228 if ( !pattern
.isEmpty() ) {
1229 setIntervalPattern(field
, pattern
);
1233 // else, looking for pattern when 'y' differ for 'dMMMM' skeleton,
1234 // first, get best match pattern "MMMd",
1235 // since there is no pattern for 'y' differs for skeleton 'MMMd',
1236 // need to look for it from skeleton 'yMMMd',
1237 // if found, adjust field width in interval pattern from
1239 UChar fieldLetter
= fgCalendarFieldToPatternLetter
[field
];
1240 if ( extendedSkeleton
) {
1241 *extendedSkeleton
= *skeleton
;
1242 *extendedBestSkeleton
= *bestSkeleton
;
1243 extendedSkeleton
->insert(0, fieldLetter
);
1244 extendedBestSkeleton
->insert(0, fieldLetter
);
1245 // for example, looking for patterns when 'y' differ for
1247 fInfo
->getIntervalPattern(*extendedBestSkeleton
,field
,pattern
,status
);
1248 if ( pattern
.isEmpty() && differenceInfo
== 0 ) {
1249 // if there is no skeleton "yMMMM" defined,
1250 // look for the best match skeleton, for example: "yMMM"
1251 const UnicodeString
* tmpBest
= fInfo
->getBestSkeleton(
1252 *extendedBestSkeleton
, differenceInfo
);
1253 if ( tmpBest
!= 0 && differenceInfo
!= -1 ) {
1254 fInfo
->getIntervalPattern(*tmpBest
, field
, pattern
, status
);
1255 bestSkeleton
= tmpBest
;
1260 if ( !pattern
.isEmpty() ) {
1261 if ( differenceInfo
!= 0 ) {
1262 UnicodeString adjustIntervalPattern
;
1263 adjustFieldWidth(*skeleton
, *bestSkeleton
, pattern
, differenceInfo
,
1264 adjustIntervalPattern
);
1265 setIntervalPattern(field
, adjustIntervalPattern
);
1267 setIntervalPattern(field
, pattern
);
1269 if ( extendedSkeleton
&& !extendedSkeleton
->isEmpty() ) {
1279 DateIntervalFormat::splitPatternInto2Part(const UnicodeString
& intervalPattern
) {
1280 UBool inQuote
= false;
1284 /* repeatedPattern used to record whether a pattern has already seen.
1285 It is a pattern applies to first calendar if it is first time seen,
1286 otherwise, it is a pattern applies to the second calendar
1288 UBool patternRepeated
[] =
1290 // A B C D E F G H I J K L M N O
1291 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
1292 // P Q R S T U V W X Y Z
1293 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
1294 // a b c d e f g h i j k l m n o
1295 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
1296 // p q r s t u v w x y z
1297 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0
1300 int8_t PATTERN_CHAR_BASE
= 0x41;
1302 /* loop through the pattern string character by character looking for
1303 * the first repeated pattern letter, which breaks the interval pattern
1307 UBool foundRepetition
= false;
1308 for (i
= 0; i
< intervalPattern
.length(); ++i
) {
1309 UChar ch
= intervalPattern
.charAt(i
);
1311 if (ch
!= prevCh
&& count
> 0) {
1312 // check the repeativeness of pattern letter
1313 UBool repeated
= patternRepeated
[(int)(prevCh
- PATTERN_CHAR_BASE
)];
1314 if ( repeated
== FALSE
) {
1315 patternRepeated
[prevCh
- PATTERN_CHAR_BASE
] = TRUE
;
1317 foundRepetition
= true;
1323 // Consecutive single quotes are a single quote literal,
1324 // either outside of quotes or between quotes
1325 if ((i
+1) < intervalPattern
.length() &&
1326 intervalPattern
.charAt(i
+1) == '\'') {
1329 inQuote
= ! inQuote
;
1332 else if (!inQuote
&& ((ch
>= 0x0061 /*'a'*/ && ch
<= 0x007A /*'z'*/)
1333 || (ch
>= 0x0041 /*'A'*/ && ch
<= 0x005A /*'Z'*/))) {
1334 // ch is a date-time pattern character
1339 // check last pattern char, distinguish
1340 // "dd MM" ( no repetition ),
1341 // "d-d"(last char repeated ), and
1342 // "d-d MM" ( repetition found )
1343 if ( count
> 0 && foundRepetition
== FALSE
) {
1344 if ( patternRepeated
[(int)(prevCh
- PATTERN_CHAR_BASE
)] == FALSE
) {
1351 static const UChar bracketedZero
[] = {0x7B,0x30,0x7D};
1352 static const UChar bracketedOne
[] = {0x7B,0x31,0x7D};
1355 DateIntervalFormat::adjustPosition(UnicodeString
& combiningPattern
, // has {0} and {1} in it
1356 UnicodeString
& pat0
, FieldPosition
& pos0
, // pattern and pos corresponding to {0}
1357 UnicodeString
& pat1
, FieldPosition
& pos1
, // pattern and pos corresponding to {1}
1358 FieldPosition
& posResult
) {
1359 int32_t index0
= combiningPattern
.indexOf(bracketedZero
, 3, 0);
1360 int32_t index1
= combiningPattern
.indexOf(bracketedOne
, 3, 0);
1361 if (index0
< 0 || index1
< 0) {
1364 if (index0
< index1
) {
1365 if (pos0
.getEndIndex() > 0) {
1366 posResult
.setBeginIndex(pos0
.getBeginIndex() + index0
);
1367 posResult
.setEndIndex(pos0
.getEndIndex() + index0
);
1368 } else if (pos1
.getEndIndex() > 0) {
1370 index1
+= pat0
.length() - 3; // adjust for pat0 replacing {0}
1371 posResult
.setBeginIndex(pos1
.getBeginIndex() + index1
);
1372 posResult
.setEndIndex(pos1
.getEndIndex() + index1
);
1375 if (pos1
.getEndIndex() > 0) {
1376 posResult
.setBeginIndex(pos1
.getBeginIndex() + index1
);
1377 posResult
.setEndIndex(pos1
.getEndIndex() + index1
);
1378 } else if (pos0
.getEndIndex() > 0) {
1380 index0
+= pat1
.length() - 3; // adjust for pat1 replacing {1}
1381 posResult
.setBeginIndex(pos0
.getBeginIndex() + index0
);
1382 posResult
.setEndIndex(pos0
.getEndIndex() + index0
);
1388 DateIntervalFormat::fallbackFormat(Calendar
& fromCalendar
,
1389 Calendar
& toCalendar
,
1390 UBool fromToOnSameDay
, // new
1391 UnicodeString
& appendTo
,
1393 UErrorCode
& status
) const {
1394 if ( U_FAILURE(status
) ) {
1397 // TODO: Fix pos handling
1398 UnicodeString fullPattern
; // for saving the pattern in fDateFormat
1399 UBool formatDatePlusTimeRange
= (fromToOnSameDay
&& fDatePattern
&& fTimePattern
);
1401 // no need delete earlierDate and laterDate since they are adopted
1402 if (formatDatePlusTimeRange
) {
1403 fDateFormat
->toPattern(fullPattern
); // save current pattern, restore later
1404 fDateFormat
->applyPattern(*fTimePattern
);
1406 FieldPosition otherPos
;
1407 otherPos
.setField(pos
.getField());
1408 UnicodeString
* earlierDate
= new UnicodeString();
1409 fDateFormat
->format(fromCalendar
, *earlierDate
, pos
);
1410 UnicodeString
* laterDate
= new UnicodeString();
1411 fDateFormat
->format(toCalendar
, *laterDate
, otherPos
);
1412 UnicodeString fallbackPattern
;
1413 fInfo
->getFallbackIntervalPattern(fallbackPattern
);
1414 adjustPosition(fallbackPattern
, *earlierDate
, pos
, *laterDate
, otherPos
, pos
);
1415 Formattable fmtArray
[2];
1416 fmtArray
[0].adoptString(earlierDate
);
1417 fmtArray
[1].adoptString(laterDate
);
1419 UnicodeString fallbackRange
;
1420 MessageFormat::format(fallbackPattern
, fmtArray
, 2, fallbackRange
, status
);
1421 if ( U_SUCCESS(status
) ) {
1422 if (!formatDatePlusTimeRange
) {
1423 appendTo
.append(fallbackRange
);
1425 // fallbackRange has just the time range, need to format the date part and combine that
1426 fDateFormat
->applyPattern(*fDatePattern
);
1427 UnicodeString
* datePortion
= new UnicodeString();
1428 otherPos
.setBeginIndex(0);
1429 otherPos
.setEndIndex(0);
1430 fDateFormat
->format(fromCalendar
, *datePortion
, otherPos
);
1431 adjustPosition(*fDateTimeFormat
, fallbackRange
, pos
, *datePortion
, otherPos
, pos
);
1432 fmtArray
[0].setString(fallbackRange
); // {0} is time range
1433 fmtArray
[1].adoptString(datePortion
); // {1} is single date portion
1434 fallbackRange
.remove();
1435 MessageFormat::format(*fDateTimeFormat
, fmtArray
, 2, fallbackRange
, status
);
1436 if ( U_SUCCESS(status
) ) {
1437 appendTo
.append(fallbackRange
);
1441 if (formatDatePlusTimeRange
) {
1442 // restore full pattern
1443 fDateFormat
->applyPattern(fullPattern
);
1452 DateIntervalFormat::fieldExistsInSkeleton(UCalendarDateFields field
,
1453 const UnicodeString
& skeleton
)
1455 const UChar fieldChar
= fgCalendarFieldToPatternLetter
[field
];
1456 return ( (skeleton
.indexOf(fieldChar
) == -1)?FALSE
:TRUE
) ;
1462 DateIntervalFormat::adjustFieldWidth(const UnicodeString
& inputSkeleton
,
1463 const UnicodeString
& bestMatchSkeleton
,
1464 const UnicodeString
& bestIntervalPattern
,
1465 int8_t differenceInfo
,
1466 UnicodeString
& adjustedPtn
) {
1467 adjustedPtn
= bestIntervalPattern
;
1468 int32_t inputSkeletonFieldWidth
[] =
1470 // A B C D E F G H I J K L M N O
1471 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
1472 // P Q R S T U V W X Y Z
1473 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
1474 // a b c d e f g h i j k l m n o
1475 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
1476 // p q r s t u v w x y z
1477 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0
1480 int32_t bestMatchSkeletonFieldWidth
[] =
1482 // A B C D E F G H I J K L M N O
1483 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
1484 // P Q R S T U V W X Y Z
1485 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
1486 // a b c d e f g h i j k l m n o
1487 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
1488 // p q r s t u v w x y z
1489 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0
1492 DateIntervalInfo::parseSkeleton(inputSkeleton
, inputSkeletonFieldWidth
);
1493 DateIntervalInfo::parseSkeleton(bestMatchSkeleton
, bestMatchSkeletonFieldWidth
);
1494 if ( differenceInfo
== 2 ) {
1495 adjustedPtn
.findAndReplace(UnicodeString((UChar
)0x76 /* v */),
1496 UnicodeString((UChar
)0x7a /* z */));
1499 UBool inQuote
= false;
1503 const int8_t PATTERN_CHAR_BASE
= 0x41;
1505 // loop through the pattern string character by character
1506 int32_t adjustedPtnLength
= adjustedPtn
.length();
1508 for (i
= 0; i
< adjustedPtnLength
; ++i
) {
1509 UChar ch
= adjustedPtn
.charAt(i
);
1510 if (ch
!= prevCh
&& count
> 0) {
1511 // check the repeativeness of pattern letter
1512 UChar skeletonChar
= prevCh
;
1513 if ( skeletonChar
== CAP_L
) {
1514 // there is no "L" (always be "M") in skeleton,
1515 // but there is "L" in pattern.
1516 // for skeleton "M+", the pattern might be "...L..."
1517 skeletonChar
= CAP_M
;
1519 int32_t fieldCount
= bestMatchSkeletonFieldWidth
[(int)(skeletonChar
- PATTERN_CHAR_BASE
)];
1520 int32_t inputFieldCount
= inputSkeletonFieldWidth
[(int)(skeletonChar
- PATTERN_CHAR_BASE
)];
1521 if ( fieldCount
== count
&& inputFieldCount
> fieldCount
) {
1522 count
= inputFieldCount
- fieldCount
;
1524 for ( j
= 0; j
< count
; ++j
) {
1525 adjustedPtn
.insert(i
, prevCh
);
1528 adjustedPtnLength
+= count
;
1533 // Consecutive single quotes are a single quote literal,
1534 // either outside of quotes or between quotes
1535 if ((i
+1) < adjustedPtn
.length() && adjustedPtn
.charAt(i
+1) == '\'') {
1538 inQuote
= ! inQuote
;
1541 else if ( ! inQuote
&& ((ch
>= 0x0061 /*'a'*/ && ch
<= 0x007A /*'z'*/)
1542 || (ch
>= 0x0041 /*'A'*/ && ch
<= 0x005A /*'Z'*/))) {
1543 // ch is a date-time pattern character
1550 // check the repeativeness of pattern letter
1551 UChar skeletonChar
= prevCh
;
1552 if ( skeletonChar
== CAP_L
) {
1553 // there is no "L" (always be "M") in skeleton,
1554 // but there is "L" in pattern.
1555 // for skeleton "M+", the pattern might be "...L..."
1556 skeletonChar
= CAP_M
;
1558 int32_t fieldCount
= bestMatchSkeletonFieldWidth
[(int)(skeletonChar
- PATTERN_CHAR_BASE
)];
1559 int32_t inputFieldCount
= inputSkeletonFieldWidth
[(int)(skeletonChar
- PATTERN_CHAR_BASE
)];
1560 if ( fieldCount
== count
&& inputFieldCount
> fieldCount
) {
1561 count
= inputFieldCount
- fieldCount
;
1563 for ( j
= 0; j
< count
; ++j
) {
1564 adjustedPtn
.append(prevCh
);
1573 DateIntervalFormat::concatSingleDate2TimeInterval(UnicodeString
& format
,
1574 const UnicodeString
& datePattern
,
1575 UCalendarDateFields field
,
1576 UErrorCode
& status
) {
1577 // following should not set wrong status
1578 int32_t itvPtnIndex
= DateIntervalInfo::calendarFieldToIntervalIndex(field
,
1580 if ( U_FAILURE(status
) ) {
1583 PatternInfo
& timeItvPtnInfo
= fIntervalPatterns
[itvPtnIndex
];
1584 if ( !timeItvPtnInfo
.firstPart
.isEmpty() ) {
1585 // UnicodeString allocated here is adopted, so no need to delete
1586 UnicodeString
* timeIntervalPattern
= new UnicodeString(timeItvPtnInfo
.firstPart
);
1587 timeIntervalPattern
->append(timeItvPtnInfo
.secondPart
);
1588 UnicodeString
* dateStr
= new UnicodeString(datePattern
);
1589 Formattable fmtArray
[2];
1590 fmtArray
[0].adoptString(timeIntervalPattern
);
1591 fmtArray
[1].adoptString(dateStr
);
1592 UnicodeString combinedPattern
;
1593 MessageFormat::format(format
, fmtArray
, 2, combinedPattern
, status
);
1594 if ( U_FAILURE(status
) ) {
1597 setIntervalPattern(field
, combinedPattern
, timeItvPtnInfo
.laterDateFirst
);
1600 // it should not happen if the interval format defined is valid
1606 DateIntervalFormat::fgCalendarFieldToPatternLetter
[] =
1608 /*GyM*/ CAP_G
, LOW_Y
, CAP_M
,
1609 /*wWd*/ LOW_W
, CAP_W
, LOW_D
,
1610 /*DEF*/ CAP_D
, CAP_E
, CAP_F
,
1611 /*ahH*/ LOW_A
, LOW_H
, CAP_H
,
1612 /*msS*/ LOW_M
, LOW_S
, CAP_S
, // MINUTE, SECOND, MILLISECOND
1613 /*z.Y*/ LOW_Z
, SPACE
, CAP_Y
, // ZONE_OFFSET, DST_OFFSET, YEAR_WOY,
1614 /*eug*/ LOW_E
, LOW_U
, LOW_G
, // DOW_LOCAL, EXTENDED_YEAR, JULIAN_DAY,
1615 /*A..*/ CAP_A
, SPACE
, SPACE
, // MILLISECONDS_IN_DAY, IS_LEAP_MONTH, FIELD_COUNT