1 // Copyright (C) 2016 and later: Unicode, Inc. and others.
2 // License & terms of use: http://www.unicode.org/copyright.html
3 /*******************************************************************************
4 * Copyright (C) 2008-2016, International Business Machines Corporation and
5 * others. All Rights Reserved.
6 *******************************************************************************
10 *******************************************************************************
13 #include "utypeinfo.h" // for 'typeid' to work
15 #include "unicode/dtitvfmt.h"
17 #if !UCONFIG_NO_FORMATTING
19 //TODO: put in compilation
20 //#define DTITVFMT_DEBUG 1
22 #include "unicode/calendar.h"
23 #include "unicode/dtptngen.h"
24 #include "unicode/dtitvinf.h"
25 #include "unicode/simpleformatter.h"
28 #include "dtitv_impl.h"
41 #define PRINTMESG(msg) { std::cout << "(" << __FILE__ << ":" << __LINE__ << ") " << msg << "\n"; }
45 static const UChar gDateFormatSkeleton[][11] = {
47 {LOW_Y, CAP_M, CAP_M, CAP_M, CAP_M, CAP_E, CAP_E, CAP_E, CAP_E, LOW_D, 0},
49 {LOW_Y, CAP_M, CAP_M, CAP_M, CAP_M, LOW_D, 0},
51 {LOW_Y, CAP_M, CAP_M, CAP_M, LOW_D, 0},
53 {LOW_Y, CAP_M, LOW_D, 0} };
56 static const char gCalendarTag[] = "calendar";
57 static const char gGregorianTag[] = "gregorian";
58 static const char gDateTimePatternsTag[] = "DateTimePatterns";
62 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};
65 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};
68 UOBJECT_DEFINE_RTTI_IMPLEMENTATION(DateIntervalFormat)
70 // Mutex, protects access to fDateFormat, fFromCalendar and fToCalendar.
71 // Needed because these data members are modified by const methods of DateIntervalFormat.
73 static UMutex gFormatterMutex = U_MUTEX_INITIALIZER;
75 DateIntervalFormat* U_EXPORT2
76 DateIntervalFormat::createInstance(const UnicodeString& skeleton,
78 return createInstance(skeleton, Locale::getDefault(), status);
82 DateIntervalFormat* U_EXPORT2
83 DateIntervalFormat::createInstance(const UnicodeString& skeleton,
90 skeleton.extract(0, skeleton.length(), result, "UTF-8");
92 ((SimpleDateFormat*)dtfmt)->toPattern(pat);
93 pat.extract(0, pat.length(), result_1, "UTF-8");
94 sprintf(mesg, "skeleton: %s; pattern: %s\n", result, result_1);
98 DateIntervalInfo* dtitvinf = new DateIntervalInfo(locale, status);
99 return create(locale, dtitvinf, &skeleton, status);
104 DateIntervalFormat* U_EXPORT2
105 DateIntervalFormat::createInstance(const UnicodeString& skeleton,
106 const DateIntervalInfo& dtitvinf,
107 UErrorCode& status) {
108 return createInstance(skeleton, Locale::getDefault(), dtitvinf, status);
112 DateIntervalFormat* U_EXPORT2
113 DateIntervalFormat::createInstance(const UnicodeString& skeleton,
114 const Locale& locale,
115 const DateIntervalInfo& dtitvinf,
116 UErrorCode& status) {
117 DateIntervalInfo* ptn = dtitvinf.clone();
118 return create(locale, ptn, &skeleton, status);
122 DateIntervalFormat::DateIntervalFormat()
127 fLocale(Locale::getRoot()),
130 fDateTimeFormat(NULL)
134 DateIntervalFormat::DateIntervalFormat(const DateIntervalFormat& itvfmt)
140 fLocale(itvfmt.fLocale),
143 fDateTimeFormat(NULL) {
149 DateIntervalFormat::operator=(const DateIntervalFormat& itvfmt) {
150 if ( this != &itvfmt ) {
153 delete fFromCalendar;
157 delete fDateTimeFormat;
159 Mutex lock(&gFormatterMutex);
160 if ( itvfmt.fDateFormat ) {
161 fDateFormat = (SimpleDateFormat*)itvfmt.fDateFormat->clone();
165 if ( itvfmt.fFromCalendar ) {
166 fFromCalendar = itvfmt.fFromCalendar->clone();
168 fFromCalendar = NULL;
170 if ( itvfmt.fToCalendar ) {
171 fToCalendar = itvfmt.fToCalendar->clone();
176 if ( itvfmt.fInfo ) {
177 fInfo = itvfmt.fInfo->clone();
181 fSkeleton = itvfmt.fSkeleton;
183 for ( i = 0; i< DateIntervalInfo::kIPI_MAX_INDEX; ++i ) {
184 fIntervalPatterns[i] = itvfmt.fIntervalPatterns[i];
186 fLocale = itvfmt.fLocale;
187 fDatePattern = (itvfmt.fDatePattern)? (UnicodeString*)itvfmt.fDatePattern->clone(): NULL;
188 fTimePattern = (itvfmt.fTimePattern)? (UnicodeString*)itvfmt.fTimePattern->clone(): NULL;
189 fDateTimeFormat = (itvfmt.fDateTimeFormat)? (UnicodeString*)itvfmt.fDateTimeFormat->clone(): NULL;
195 DateIntervalFormat::~DateIntervalFormat() {
198 delete fFromCalendar;
202 delete fDateTimeFormat;
207 DateIntervalFormat::clone(void) const {
208 return new DateIntervalFormat(*this);
213 DateIntervalFormat::operator==(const Format& other) const {
214 if (typeid(*this) != typeid(other)) {return FALSE;}
215 const DateIntervalFormat* fmt = (DateIntervalFormat*)&other;
216 if (this == fmt) {return TRUE;}
217 if (!Format::operator==(other)) {return FALSE;}
218 if ((fInfo != fmt->fInfo) && (fInfo == NULL || fmt->fInfo == NULL)) {return FALSE;}
219 if (fInfo && fmt->fInfo && (*fInfo != *fmt->fInfo )) {return FALSE;}
221 Mutex lock(&gFormatterMutex);
222 if (fDateFormat != fmt->fDateFormat && (fDateFormat == NULL || fmt->fDateFormat == NULL)) {return FALSE;}
223 if (fDateFormat && fmt->fDateFormat && (*fDateFormat != *fmt->fDateFormat)) {return FALSE;}
225 // note: fFromCalendar and fToCalendar hold no persistent state, and therefore do not participate in operator ==.
226 // fDateFormat has the master calendar for the DateIntervalFormat.
227 if (fSkeleton != fmt->fSkeleton) {return FALSE;}
228 if (fDatePattern != fmt->fDatePattern && (fDatePattern == NULL || fmt->fDatePattern == NULL)) {return FALSE;}
229 if (fDatePattern && fmt->fDatePattern && (*fDatePattern != *fmt->fDatePattern)) {return FALSE;}
230 if (fTimePattern != fmt->fTimePattern && (fTimePattern == NULL || fmt->fTimePattern == NULL)) {return FALSE;}
231 if (fTimePattern && fmt->fTimePattern && (*fTimePattern != *fmt->fTimePattern)) {return FALSE;}
232 if (fDateTimeFormat != fmt->fDateTimeFormat && (fDateTimeFormat == NULL || fmt->fDateTimeFormat == NULL)) {return FALSE;}
233 if (fDateTimeFormat && fmt->fDateTimeFormat && (*fDateTimeFormat != *fmt->fDateTimeFormat)) {return FALSE;}
234 if (fLocale != fmt->fLocale) {return FALSE;}
236 for (int32_t i = 0; i< DateIntervalInfo::kIPI_MAX_INDEX; ++i ) {
237 if (fIntervalPatterns[i].firstPart != fmt->fIntervalPatterns[i].firstPart) {return FALSE;}
238 if (fIntervalPatterns[i].secondPart != fmt->fIntervalPatterns[i].secondPart ) {return FALSE;}
239 if (fIntervalPatterns[i].laterDateFirst != fmt->fIntervalPatterns[i].laterDateFirst) {return FALSE;}
246 DateIntervalFormat::format(const Formattable& obj,
247 UnicodeString& appendTo,
248 FieldPosition& fieldPosition,
249 UErrorCode& status) const {
250 if ( U_FAILURE(status) ) {
254 if ( obj.getType() == Formattable::kObject ) {
255 const UObject* formatObj = obj.getObject();
256 const DateInterval* interval = dynamic_cast<const DateInterval*>(formatObj);
257 if (interval != NULL) {
258 return format(interval, appendTo, fieldPosition, status);
261 status = U_ILLEGAL_ARGUMENT_ERROR;
267 DateIntervalFormat::format(const DateInterval* dtInterval,
268 UnicodeString& appendTo,
269 FieldPosition& fieldPosition,
270 UErrorCode& status) const {
271 if ( U_FAILURE(status) ) {
274 if (fFromCalendar == NULL || fToCalendar == NULL || fDateFormat == NULL || fInfo == NULL) {
275 status = U_INVALID_STATE_ERROR;
279 Mutex lock(&gFormatterMutex);
280 fFromCalendar->setTime(dtInterval->getFromDate(), status);
281 fToCalendar->setTime(dtInterval->getToDate(), status);
282 return formatImpl(*fFromCalendar, *fToCalendar, appendTo,fieldPosition, status);
287 DateIntervalFormat::format(Calendar& fromCalendar,
288 Calendar& toCalendar,
289 UnicodeString& appendTo,
291 UErrorCode& status) const {
292 Mutex lock(&gFormatterMutex);
293 return formatImpl(fromCalendar, toCalendar, appendTo, pos, status);
298 DateIntervalFormat::formatImpl(Calendar& fromCalendar,
299 Calendar& toCalendar,
300 UnicodeString& appendTo,
302 UErrorCode& status) const {
303 if ( U_FAILURE(status) ) {
307 // not support different calendar types and time zones
308 //if ( fromCalendar.getType() != toCalendar.getType() ) {
309 if ( !fromCalendar.isEquivalentTo(toCalendar) ) {
310 status = U_ILLEGAL_ARGUMENT_ERROR;
314 // First, find the largest different calendar field.
315 UCalendarDateFields field = UCAL_FIELD_COUNT;
317 if ( fromCalendar.get(UCAL_ERA,status) != toCalendar.get(UCAL_ERA,status)) {
319 } else if ( fromCalendar.get(UCAL_YEAR, status) !=
320 toCalendar.get(UCAL_YEAR, status) ) {
322 } else if ( fromCalendar.get(UCAL_MONTH, status) !=
323 toCalendar.get(UCAL_MONTH, status) ) {
325 } else if ( fromCalendar.get(UCAL_DATE, status) !=
326 toCalendar.get(UCAL_DATE, status) ) {
328 } else if ( fromCalendar.get(UCAL_AM_PM, status) !=
329 toCalendar.get(UCAL_AM_PM, status) ) {
331 } else if ( fromCalendar.get(UCAL_HOUR, status) !=
332 toCalendar.get(UCAL_HOUR, status) ) {
334 } else if ( fromCalendar.get(UCAL_MINUTE, status) !=
335 toCalendar.get(UCAL_MINUTE, status) ) {
337 } else if ( fromCalendar.get(UCAL_SECOND, status) !=
338 toCalendar.get(UCAL_SECOND, status) ) {
342 if ( U_FAILURE(status) ) {
345 if ( field == UCAL_FIELD_COUNT ) {
346 /* ignore the millisecond etc. small fields' difference.
347 * use single date when all the above are the same.
349 return fDateFormat->format(fromCalendar, appendTo, pos);
351 UBool fromToOnSameDay = (field==UCAL_AM_PM || field==UCAL_HOUR || field==UCAL_MINUTE || field==UCAL_SECOND);
353 // following call should not set wrong status,
354 // all the pass-in fields are valid till here
355 int32_t itvPtnIndex = DateIntervalInfo::calendarFieldToIntervalIndex(field,
357 const PatternInfo& intervalPattern = fIntervalPatterns[itvPtnIndex];
359 if ( intervalPattern.firstPart.isEmpty() &&
360 intervalPattern.secondPart.isEmpty() ) {
361 if ( fDateFormat->isFieldUnitIgnored(field) ) {
362 /* the largest different calendar field is small than
363 * the smallest calendar field in pattern,
364 * return single date format.
366 return fDateFormat->format(fromCalendar, appendTo, pos);
368 return fallbackFormat(fromCalendar, toCalendar, fromToOnSameDay, appendTo, pos, status);
370 // If the first part in interval pattern is empty,
371 // the 2nd part of it saves the full-pattern used in fall-back.
372 // For a 'real' interval pattern, the first part will never be empty.
373 if ( intervalPattern.firstPart.isEmpty() ) {
375 UnicodeString originalPattern;
376 fDateFormat->toPattern(originalPattern);
377 fDateFormat->applyPattern(intervalPattern.secondPart);
378 appendTo = fallbackFormat(fromCalendar, toCalendar, fromToOnSameDay, appendTo, pos, status);
379 fDateFormat->applyPattern(originalPattern);
384 if ( intervalPattern.laterDateFirst ) {
385 firstCal = &toCalendar;
386 secondCal = &fromCalendar;
388 firstCal = &fromCalendar;
389 secondCal = &toCalendar;
391 // break the interval pattern into 2 parts,
392 // first part should not be empty,
393 UnicodeString originalPattern;
394 fDateFormat->toPattern(originalPattern);
395 fDateFormat->applyPattern(intervalPattern.firstPart);
396 fDateFormat->format(*firstCal, appendTo, pos);
397 if ( !intervalPattern.secondPart.isEmpty() ) {
398 fDateFormat->applyPattern(intervalPattern.secondPart);
399 FieldPosition otherPos;
400 otherPos.setField(pos.getField());
401 fDateFormat->format(*secondCal, appendTo, otherPos);
402 if (pos.getEndIndex() == 0 && otherPos.getEndIndex() > 0) {
406 fDateFormat->applyPattern(originalPattern);
413 DateIntervalFormat::parseObject(const UnicodeString& /* source */,
414 Formattable& /* result */,
415 ParsePosition& /* parse_pos */) const {
416 // parseObject(const UnicodeString&, Formattable&, UErrorCode&) const
417 // will set status as U_INVALID_FORMAT_ERROR if
418 // parse_pos is still 0
424 const DateIntervalInfo*
425 DateIntervalFormat::getDateIntervalInfo() const {
431 DateIntervalFormat::setDateIntervalInfo(const DateIntervalInfo& newItvPattern,
432 UErrorCode& status) {
434 fInfo = new DateIntervalInfo(newItvPattern);
436 // Delete patterns that get reset by initializePattern
441 delete fDateTimeFormat;
442 fDateTimeFormat = NULL;
445 initializePattern(status);
452 DateIntervalFormat::getDateFormat() const {
458 DateIntervalFormat::adoptTimeZone(TimeZone* zone)
460 if (fDateFormat != NULL) {
461 fDateFormat->adoptTimeZone(zone);
463 // The fDateFormat has the master calendar for the DateIntervalFormat and has
464 // ownership of any adopted TimeZone; fFromCalendar and fToCalendar are internal
465 // work clones of that calendar (and should not also be given ownership of the
466 // adopted TimeZone).
468 fFromCalendar->setTimeZone(*zone);
471 fToCalendar->setTimeZone(*zone);
476 DateIntervalFormat::setTimeZone(const TimeZone& zone)
478 if (fDateFormat != NULL) {
479 fDateFormat->setTimeZone(zone);
481 // The fDateFormat has the master calendar for the DateIntervalFormat;
482 // fFromCalendar and fToCalendar are internal work clones of that calendar.
484 fFromCalendar->setTimeZone(zone);
487 fToCalendar->setTimeZone(zone);
492 DateIntervalFormat::getTimeZone() const
494 if (fDateFormat != NULL) {
495 Mutex lock(&gFormatterMutex);
496 return fDateFormat->getTimeZone();
498 // If fDateFormat is NULL (unexpected), create default timezone.
499 return *(TimeZone::createDefault());
502 DateIntervalFormat::DateIntervalFormat(const Locale& locale,
503 DateIntervalInfo* dtItvInfo,
504 const UnicodeString* skeleton,
513 fDateTimeFormat(NULL)
515 LocalPointer<DateIntervalInfo> info(dtItvInfo, status);
516 LocalPointer<SimpleDateFormat> dtfmt(static_cast<SimpleDateFormat *>(
517 DateFormat::createInstanceForSkeleton(*skeleton, locale, status)), status);
518 if (U_FAILURE(status)) {
523 fSkeleton = *skeleton;
525 fInfo = info.orphan();
526 fDateFormat = dtfmt.orphan();
527 if ( fDateFormat->getCalendar() ) {
528 fFromCalendar = fDateFormat->getCalendar()->clone();
529 fToCalendar = fDateFormat->getCalendar()->clone();
531 initializePattern(status);
534 DateIntervalFormat* U_EXPORT2
535 DateIntervalFormat::create(const Locale& locale,
536 DateIntervalInfo* dtitvinf,
537 const UnicodeString* skeleton,
538 UErrorCode& status) {
539 DateIntervalFormat* f = new DateIntervalFormat(locale, dtitvinf,
542 status = U_MEMORY_ALLOCATION_ERROR;
544 } else if ( U_FAILURE(status) ) {
545 // safe to delete f, although nothing acutally is saved
555 * Initialize interval patterns locale to this formatter
557 * This code is a bit complicated since
558 * 1. the interval patterns saved in resource bundle files are interval
559 * patterns based on date or time only.
560 * It does not have interval patterns based on both date and time.
561 * Interval patterns on both date and time are algorithm generated.
563 * For example, it has interval patterns on skeleton "dMy" and "hm",
564 * but it does not have interval patterns on skeleton "dMyhm".
566 * The rule to genearte interval patterns for both date and time skeleton are
567 * 1) when the year, month, or day differs, concatenate the two original
568 * expressions with a separator between,
569 * For example, interval pattern from "Jan 10, 2007 10:10 am"
570 * to "Jan 11, 2007 10:10am" is
571 * "Jan 10, 2007 10:10 am - Jan 11, 2007 10:10am"
573 * 2) otherwise, present the date followed by the range expression
575 * For example, interval pattern from "Jan 10, 2007 10:10 am"
576 * to "Jan 10, 2007 11:10am" is
577 * "Jan 10, 2007 10:10 am - 11:10am"
579 * 2. even a pattern does not request a certion calendar field,
580 * the interval pattern needs to include such field if such fields are
581 * different between 2 dates.
582 * For example, a pattern/skeleton is "hm", but the interval pattern
583 * includes year, month, and date when year, month, and date differs.
585 * @param status output param set to success/failure code on exit
589 DateIntervalFormat::initializePattern(UErrorCode& status) {
590 if ( U_FAILURE(status) ) {
593 const Locale& locale = fDateFormat->getSmpFmtLocale();
594 if ( fSkeleton.isEmpty() ) {
595 UnicodeString fullPattern;
596 fDateFormat->toPattern(fullPattern);
597 #ifdef DTITVFMT_DEBUG
601 fSkeleton.extract(0, fSkeleton.length(), result, "UTF-8");
602 sprintf(mesg, "in getBestSkeleton: fSkeleton: %s; \n", result);
605 // fSkeleton is already set by createDateIntervalInstance()
606 // or by createInstance(UnicodeString skeleton, .... )
607 fSkeleton = DateTimePatternGenerator::staticGetSkeleton(
608 fullPattern, status);
609 if ( U_FAILURE(status) ) {
614 // initialize the fIntervalPattern ordering
616 for ( i = 0; i < DateIntervalInfo::kIPI_MAX_INDEX; ++i ) {
617 fIntervalPatterns[i].laterDateFirst = fInfo->getDefaultOrder();
620 /* Check whether the skeleton is a combination of date and time.
621 * For the complication reason 1 explained above.
623 UnicodeString dateSkeleton;
624 UnicodeString timeSkeleton;
625 UnicodeString normalizedTimeSkeleton;
626 UnicodeString normalizedDateSkeleton;
629 /* the difference between time skeleton and normalizedTimeSkeleton are:
630 * 1. (Formerly, normalized time skeleton folded 'H' to 'h'; no longer true)
631 * 2. 'a' is omitted in normalized time skeleton.
632 * 3. there is only one appearance for 'h' or 'H', 'm','v', 'z' in normalized
635 * The difference between date skeleton and normalizedDateSkeleton are:
636 * 1. both 'y' and 'd' appear only once in normalizeDateSkeleton
637 * 2. 'E' and 'EE' are normalized into 'EEE'
638 * 3. 'MM' is normalized into 'M'
640 getDateTimeSkeleton(fSkeleton, dateSkeleton, normalizedDateSkeleton,
641 timeSkeleton, normalizedTimeSkeleton);
643 #ifdef DTITVFMT_DEBUG
647 fSkeleton.extract(0, fSkeleton.length(), result, "UTF-8");
648 sprintf(mesg, "in getBestSkeleton: fSkeleton: %s; \n", result);
652 // move this up here since we need it for fallbacks
653 if ( timeSkeleton.length() > 0 && dateSkeleton.length() > 0 ) {
654 // Need the Date/Time pattern for concatenation of the date
655 // with the time interval.
656 // The date/time pattern ( such as {0} {1} ) is saved in
657 // calendar, that is why need to get the CalendarData here.
658 LocalUResourceBundlePointer dateTimePatternsRes(ures_open(NULL, locale.getBaseName(), &status));
659 ures_getByKey(dateTimePatternsRes.getAlias(), gCalendarTag,
660 dateTimePatternsRes.getAlias(), &status);
661 ures_getByKeyWithFallback(dateTimePatternsRes.getAlias(), gGregorianTag,
662 dateTimePatternsRes.getAlias(), &status);
663 ures_getByKeyWithFallback(dateTimePatternsRes.getAlias(), gDateTimePatternsTag,
664 dateTimePatternsRes.getAlias(), &status);
666 int32_t dateTimeFormatLength;
667 const UChar* dateTimeFormat = ures_getStringByIndex(
668 dateTimePatternsRes.getAlias(),
669 (int32_t)DateFormat::kDateTime,
670 &dateTimeFormatLength, &status);
671 if ( U_SUCCESS(status) && dateTimeFormatLength >= 3 ) {
672 fDateTimeFormat = new UnicodeString(dateTimeFormat, dateTimeFormatLength);
676 UBool found = setSeparateDateTimePtn(normalizedDateSkeleton,
677 normalizedTimeSkeleton);
679 // for skeletons with seconds, found is false and we enter this block
680 if ( found == false ) {
682 // TODO: if user asks "m"(minute), but "d"(day) differ
683 if ( timeSkeleton.length() != 0 ) {
684 if ( dateSkeleton.length() == 0 ) {
686 timeSkeleton.insert(0, gDateFormatSkeleton[DateFormat::kShort], -1);
687 UnicodeString pattern = DateFormat::getBestPattern(
688 locale, timeSkeleton, status);
689 if ( U_FAILURE(status) ) {
692 // for fall back interval patterns,
693 // the first part of the pattern is empty,
694 // the second part of the pattern is the full-pattern
695 // should be used in fall-back.
696 setPatternInfo(UCAL_DATE, NULL, &pattern, fInfo->getDefaultOrder());
697 setPatternInfo(UCAL_MONTH, NULL, &pattern, fInfo->getDefaultOrder());
698 setPatternInfo(UCAL_YEAR, NULL, &pattern, fInfo->getDefaultOrder());
706 } // end of skeleton not found
707 // interval patterns for skeleton are found in resource
708 if ( timeSkeleton.length() == 0 ) {
710 } else if ( dateSkeleton.length() == 0 ) {
712 timeSkeleton.insert(0, gDateFormatSkeleton[DateFormat::kShort], -1);
713 UnicodeString pattern = DateFormat::getBestPattern(
714 locale, timeSkeleton, status);
715 if ( U_FAILURE(status) ) {
718 // for fall back interval patterns,
719 // the first part of the pattern is empty,
720 // the second part of the pattern is the full-pattern
721 // should be used in fall-back.
722 setPatternInfo(UCAL_DATE, NULL, &pattern, fInfo->getDefaultOrder());
723 setPatternInfo(UCAL_MONTH, NULL, &pattern, fInfo->getDefaultOrder());
724 setPatternInfo(UCAL_YEAR, NULL, &pattern, fInfo->getDefaultOrder());
727 * 1) when the year, month, or day differs,
728 * concatenate the two original expressions with a separator between,
729 * 2) otherwise, present the date followed by the
730 * range expression for the time.
733 * 1) when the year, month, or day differs,
734 * concatenate the two original expressions with a separator between,
736 // if field exists, use fall back
737 UnicodeString skeleton = fSkeleton;
738 if ( !fieldExistsInSkeleton(UCAL_DATE, dateSkeleton) ) {
739 // prefix skeleton with 'd'
740 skeleton.insert(0, LOW_D);
741 setFallbackPattern(UCAL_DATE, skeleton, status);
743 if ( !fieldExistsInSkeleton(UCAL_MONTH, dateSkeleton) ) {
744 // then prefix skeleton with 'M'
745 skeleton.insert(0, CAP_M);
746 setFallbackPattern(UCAL_MONTH, skeleton, status);
748 if ( !fieldExistsInSkeleton(UCAL_YEAR, dateSkeleton) ) {
749 // then prefix skeleton with 'y'
750 skeleton.insert(0, LOW_Y);
751 setFallbackPattern(UCAL_YEAR, skeleton, status);
755 * 2) otherwise, present the date followed by the
756 * range expression for the time.
759 if ( fDateTimeFormat == NULL ) {
760 // earlier failure getting dateTimeFormat
764 UnicodeString datePattern = DateFormat::getBestPattern(
765 locale, dateSkeleton, status);
767 concatSingleDate2TimeInterval(*fDateTimeFormat, datePattern, UCAL_AM_PM, status);
768 concatSingleDate2TimeInterval(*fDateTimeFormat, datePattern, UCAL_HOUR, status);
769 concatSingleDate2TimeInterval(*fDateTimeFormat, datePattern, UCAL_MINUTE, status);
776 DateIntervalFormat::getDateTimeSkeleton(const UnicodeString& skeleton,
777 UnicodeString& dateSkeleton,
778 UnicodeString& normalizedDateSkeleton,
779 UnicodeString& timeSkeleton,
780 UnicodeString& normalizedTimeSkeleton) {
781 // dateSkeleton follows the sequence of y*M*E*d*
782 // timeSkeleton follows the sequence of hm*[v|z]?
794 for (i = 0; i < skeleton.length(); ++i) {
795 UChar ch = skeleton[i];
798 dateSkeleton.append(ch);
802 dateSkeleton.append(ch);
806 dateSkeleton.append(ch);
810 dateSkeleton.append(ch);
829 normalizedDateSkeleton.append(ch);
830 dateSkeleton.append(ch);
833 // 'a' is implicitly handled
834 timeSkeleton.append(ch);
837 timeSkeleton.append(ch);
841 timeSkeleton.append(ch);
845 timeSkeleton.append(ch);
850 timeSkeleton.append(ch);
854 timeSkeleton.append(ch);
864 timeSkeleton.append(ch);
865 normalizedTimeSkeleton.append(ch);
870 /* generate normalized form for date*/
872 for (i = 0; i < yCount; ++i) {
873 normalizedDateSkeleton.append(LOW_Y);
878 normalizedDateSkeleton.append(CAP_M);
881 for ( i = 0; i < MCount && i < MAX_M_COUNT; ++i ) {
882 normalizedDateSkeleton.append(CAP_M);
888 normalizedDateSkeleton.append(CAP_E);
891 for ( i = 0; i < ECount && i < MAX_E_COUNT; ++i ) {
892 normalizedDateSkeleton.append(CAP_E);
897 normalizedDateSkeleton.append(LOW_D);
900 /* generate normalized form for time */
902 normalizedTimeSkeleton.append(CAP_H);
904 else if ( hCount != 0 ) {
905 normalizedTimeSkeleton.append(LOW_H);
908 normalizedTimeSkeleton.append(LOW_M);
911 normalizedTimeSkeleton.append(LOW_Z);
914 normalizedTimeSkeleton.append(LOW_V);
920 * Generate date or time interval pattern from resource,
921 * and set them into the interval pattern locale to this formatter.
923 * It needs to handle the following:
924 * 1. need to adjust field width.
925 * For example, the interval patterns saved in DateIntervalInfo
926 * includes "dMMMy", but not "dMMMMy".
927 * Need to get interval patterns for dMMMMy from dMMMy.
928 * Another example, the interval patterns saved in DateIntervalInfo
929 * includes "hmv", but not "hmz".
930 * Need to get interval patterns for "hmz' from 'hmv'
932 * 2. there might be no pattern for 'y' differ for skeleton "Md",
933 * in order to get interval patterns for 'y' differ,
934 * need to look for it from skeleton 'yMd'
936 * @param dateSkeleton normalized date skeleton
937 * @param timeSkeleton normalized time skeleton
938 * @return whether the resource is found for the skeleton.
939 * TRUE if interval pattern found for the skeleton,
944 DateIntervalFormat::setSeparateDateTimePtn(
945 const UnicodeString& dateSkeleton,
946 const UnicodeString& timeSkeleton) {
947 const UnicodeString* skeleton;
948 // if both date and time skeleton present,
949 // the final interval pattern might include time interval patterns
950 // ( when, am_pm, hour, minute differ ),
951 // but not date interval patterns ( when year, month, day differ ).
952 // For year/month/day differ, it falls back to fall-back pattern.
953 if ( timeSkeleton.length() != 0 ) {
954 skeleton = &timeSkeleton;
956 skeleton = &dateSkeleton;
959 /* interval patterns for skeleton "dMMMy" (but not "dMMMMy")
960 * are defined in resource,
961 * interval patterns for skeleton "dMMMMy" are calculated by
962 * 1. get the best match skeleton for "dMMMMy", which is "dMMMy"
963 * 2. get the interval patterns for "dMMMy",
964 * 3. extend "MMM" to "MMMM" in above interval patterns for "dMMMMy"
965 * getBestSkeleton() is step 1.
967 // best skeleton, and the difference information
968 int8_t differenceInfo = 0;
969 const UnicodeString* bestSkeleton = fInfo->getBestSkeleton(*skeleton,
971 /* best skeleton could be NULL.
972 For example: in "ca" resource file,
973 interval format is defined as following
975 fallback{"{0} - {1}"}
977 there is no skeletons/interval patterns defined,
978 and the best skeleton match could be NULL
980 if ( bestSkeleton == NULL ) {
984 // Set patterns for fallback use, need to do this
985 // before returning if differenceInfo == -1
987 if ( dateSkeleton.length() != 0) {
988 status = U_ZERO_ERROR;
989 fDatePattern = new UnicodeString(DateFormat::getBestPattern(
990 fLocale, dateSkeleton, status));
992 if ( timeSkeleton.length() != 0) {
993 status = U_ZERO_ERROR;
994 fTimePattern = new UnicodeString(DateFormat::getBestPattern(
995 fLocale, timeSkeleton, status));
999 // 0 means the best matched skeleton is the same as input skeleton
1000 // 1 means the fields are the same, but field width are different
1001 // 2 means the only difference between fields are v/z,
1002 // -1 means there are other fields difference
1003 // (this will happen, for instance, if the supplied skeleton has seconds,
1004 // but no skeletons in the intervalFormats data do)
1005 if ( differenceInfo == -1 ) {
1006 // skeleton has different fields, not only v/z difference
1010 if ( timeSkeleton.length() == 0 ) {
1011 UnicodeString extendedSkeleton;
1012 UnicodeString extendedBestSkeleton;
1013 // only has date skeleton
1014 setIntervalPattern(UCAL_DATE, skeleton, bestSkeleton, differenceInfo,
1015 &extendedSkeleton, &extendedBestSkeleton);
1017 UBool extended = setIntervalPattern(UCAL_MONTH, skeleton, bestSkeleton,
1019 &extendedSkeleton, &extendedBestSkeleton);
1022 bestSkeleton = &extendedBestSkeleton;
1023 skeleton = &extendedSkeleton;
1025 setIntervalPattern(UCAL_YEAR, skeleton, bestSkeleton, differenceInfo,
1026 &extendedSkeleton, &extendedBestSkeleton);
1028 setIntervalPattern(UCAL_MINUTE, skeleton, bestSkeleton, differenceInfo);
1029 setIntervalPattern(UCAL_HOUR, skeleton, bestSkeleton, differenceInfo);
1030 setIntervalPattern(UCAL_AM_PM, skeleton, bestSkeleton, differenceInfo);
1038 DateIntervalFormat::setFallbackPattern(UCalendarDateFields field,
1039 const UnicodeString& skeleton,
1040 UErrorCode& status) {
1041 if ( U_FAILURE(status) ) {
1044 UnicodeString pattern = DateFormat::getBestPattern(
1045 fLocale, skeleton, status);
1046 if ( U_FAILURE(status) ) {
1049 setPatternInfo(field, NULL, &pattern, fInfo->getDefaultOrder());
1056 DateIntervalFormat::setPatternInfo(UCalendarDateFields field,
1057 const UnicodeString* firstPart,
1058 const UnicodeString* secondPart,
1059 UBool laterDateFirst) {
1060 // for fall back interval patterns,
1061 // the first part of the pattern is empty,
1062 // the second part of the pattern is the full-pattern
1063 // should be used in fall-back.
1064 UErrorCode status = U_ZERO_ERROR;
1065 // following should not set any wrong status.
1066 int32_t itvPtnIndex = DateIntervalInfo::calendarFieldToIntervalIndex(field,
1068 if ( U_FAILURE(status) ) {
1071 PatternInfo& ptn = fIntervalPatterns[itvPtnIndex];
1073 ptn.firstPart = *firstPart;
1076 ptn.secondPart = *secondPart;
1078 ptn.laterDateFirst = laterDateFirst;
1082 DateIntervalFormat::setIntervalPattern(UCalendarDateFields field,
1083 const UnicodeString& intervalPattern) {
1084 UBool order = fInfo->getDefaultOrder();
1085 setIntervalPattern(field, intervalPattern, order);
1090 DateIntervalFormat::setIntervalPattern(UCalendarDateFields field,
1091 const UnicodeString& intervalPattern,
1092 UBool laterDateFirst) {
1093 const UnicodeString* pattern = &intervalPattern;
1094 UBool order = laterDateFirst;
1095 // check for "latestFirst:" or "earliestFirst:" prefix
1096 int8_t prefixLength = UPRV_LENGTHOF(gLaterFirstPrefix);
1097 int8_t earliestFirstLength = UPRV_LENGTHOF(gEarlierFirstPrefix);
1098 UnicodeString realPattern;
1099 if ( intervalPattern.startsWith(gLaterFirstPrefix, prefixLength) ) {
1101 intervalPattern.extract(prefixLength,
1102 intervalPattern.length() - prefixLength,
1104 pattern = &realPattern;
1105 } else if ( intervalPattern.startsWith(gEarlierFirstPrefix,
1106 earliestFirstLength) ) {
1108 intervalPattern.extract(earliestFirstLength,
1109 intervalPattern.length() - earliestFirstLength,
1111 pattern = &realPattern;
1114 int32_t splitPoint = splitPatternInto2Part(*pattern);
1116 UnicodeString firstPart;
1117 UnicodeString secondPart;
1118 pattern->extract(0, splitPoint, firstPart);
1119 if ( splitPoint < pattern->length() ) {
1120 pattern->extract(splitPoint, pattern->length()-splitPoint, secondPart);
1122 setPatternInfo(field, &firstPart, &secondPart, order);
1129 * Generate interval pattern from existing resource
1131 * It not only save the interval patterns,
1132 * but also return the extended skeleton and its best match skeleton.
1134 * @param field largest different calendar field
1135 * @param skeleton skeleton
1136 * @param bestSkeleton the best match skeleton which has interval pattern
1137 * defined in resource
1138 * @param differenceInfo the difference between skeleton and best skeleton
1139 * 0 means the best matched skeleton is the same as input skeleton
1140 * 1 means the fields are the same, but field width are different
1141 * 2 means the only difference between fields are v/z,
1142 * -1 means there are other fields difference
1144 * @param extendedSkeleton extended skeleton
1145 * @param extendedBestSkeleton extended best match skeleton
1146 * @return whether the interval pattern is found
1147 * through extending skeleton or not.
1148 * TRUE if interval pattern is found by
1149 * extending skeleton, FALSE otherwise.
1153 DateIntervalFormat::setIntervalPattern(UCalendarDateFields field,
1154 const UnicodeString* skeleton,
1155 const UnicodeString* bestSkeleton,
1156 int8_t differenceInfo,
1157 UnicodeString* extendedSkeleton,
1158 UnicodeString* extendedBestSkeleton) {
1159 UErrorCode status = U_ZERO_ERROR;
1160 // following getIntervalPattern() should not generate error status
1161 UnicodeString pattern;
1162 fInfo->getIntervalPattern(*bestSkeleton, field, pattern, status);
1163 if ( pattern.isEmpty() ) {
1165 if ( SimpleDateFormat::isFieldUnitIgnored(*bestSkeleton, field) ) {
1166 // do nothing, format will handle it
1170 // for 24 hour system, interval patterns in resource file
1171 // might not include pattern when am_pm differ,
1172 // which should be the same as hour differ.
1173 // add it here for simplicity
1174 if ( field == UCAL_AM_PM ) {
1175 fInfo->getIntervalPattern(*bestSkeleton, UCAL_HOUR, pattern,status);
1176 if ( !pattern.isEmpty() ) {
1177 setIntervalPattern(field, pattern);
1181 // else, looking for pattern when 'y' differ for 'dMMMM' skeleton,
1182 // first, get best match pattern "MMMd",
1183 // since there is no pattern for 'y' differs for skeleton 'MMMd',
1184 // need to look for it from skeleton 'yMMMd',
1185 // if found, adjust field width in interval pattern from
1187 UChar fieldLetter = fgCalendarFieldToPatternLetter[field];
1188 if ( extendedSkeleton ) {
1189 *extendedSkeleton = *skeleton;
1190 *extendedBestSkeleton = *bestSkeleton;
1191 extendedSkeleton->insert(0, fieldLetter);
1192 extendedBestSkeleton->insert(0, fieldLetter);
1193 // for example, looking for patterns when 'y' differ for
1195 fInfo->getIntervalPattern(*extendedBestSkeleton,field,pattern,status);
1196 if ( pattern.isEmpty() && differenceInfo == 0 ) {
1197 // if there is no skeleton "yMMMM" defined,
1198 // look for the best match skeleton, for example: "yMMM"
1199 const UnicodeString* tmpBest = fInfo->getBestSkeleton(
1200 *extendedBestSkeleton, differenceInfo);
1201 if ( tmpBest != 0 && differenceInfo != -1 ) {
1202 fInfo->getIntervalPattern(*tmpBest, field, pattern, status);
1203 bestSkeleton = tmpBest;
1208 if ( !pattern.isEmpty() ) {
1209 if ( differenceInfo != 0 ) {
1210 UnicodeString adjustIntervalPattern;
1211 adjustFieldWidth(*skeleton, *bestSkeleton, pattern, differenceInfo,
1212 adjustIntervalPattern);
1213 setIntervalPattern(field, adjustIntervalPattern);
1215 setIntervalPattern(field, pattern);
1217 if ( extendedSkeleton && !extendedSkeleton->isEmpty() ) {
1227 DateIntervalFormat::splitPatternInto2Part(const UnicodeString& intervalPattern) {
1228 UBool inQuote = false;
1232 /* repeatedPattern used to record whether a pattern has already seen.
1233 It is a pattern applies to first calendar if it is first time seen,
1234 otherwise, it is a pattern applies to the second calendar
1236 UBool patternRepeated[] =
1238 // A B C D E F G H I J K L M N O
1239 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
1240 // P Q R S T U V W X Y Z
1241 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
1242 // a b c d e f g h i j k l m n o
1243 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
1244 // p q r s t u v w x y z
1245 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0
1248 int8_t PATTERN_CHAR_BASE = 0x41;
1250 /* loop through the pattern string character by character looking for
1251 * the first repeated pattern letter, which breaks the interval pattern
1255 UBool foundRepetition = false;
1256 for (i = 0; i < intervalPattern.length(); ++i) {
1257 UChar ch = intervalPattern.charAt(i);
1259 if (ch != prevCh && count > 0) {
1260 // check the repeativeness of pattern letter
1261 UBool repeated = patternRepeated[(int)(prevCh - PATTERN_CHAR_BASE)];
1262 if ( repeated == FALSE ) {
1263 patternRepeated[prevCh - PATTERN_CHAR_BASE] = TRUE;
1265 foundRepetition = true;
1270 if (ch == 0x0027 /*'*/) {
1271 // Consecutive single quotes are a single quote literal,
1272 // either outside of quotes or between quotes
1273 if ((i+1) < intervalPattern.length() &&
1274 intervalPattern.charAt(i+1) == 0x0027 /*'*/) {
1277 inQuote = ! inQuote;
1280 else if (!inQuote && ((ch >= 0x0061 /*'a'*/ && ch <= 0x007A /*'z'*/)
1281 || (ch >= 0x0041 /*'A'*/ && ch <= 0x005A /*'Z'*/))) {
1282 // ch is a date-time pattern character
1287 // check last pattern char, distinguish
1288 // "dd MM" ( no repetition ),
1289 // "d-d"(last char repeated ), and
1290 // "d-d MM" ( repetition found )
1291 if ( count > 0 && foundRepetition == FALSE ) {
1292 if ( patternRepeated[(int)(prevCh - PATTERN_CHAR_BASE)] == FALSE ) {
1299 static const UChar bracketedZero[] = {0x7B,0x30,0x7D};
1300 static const UChar bracketedOne[] = {0x7B,0x31,0x7D};
1303 DateIntervalFormat::adjustPosition(UnicodeString& combiningPattern, // has {0} and {1} in it
1304 UnicodeString& pat0, FieldPosition& pos0, // pattern and pos corresponding to {0}
1305 UnicodeString& pat1, FieldPosition& pos1, // pattern and pos corresponding to {1}
1306 FieldPosition& posResult) {
1307 int32_t index0 = combiningPattern.indexOf(bracketedZero, 3, 0);
1308 int32_t index1 = combiningPattern.indexOf(bracketedOne, 3, 0);
1309 if (index0 < 0 || index1 < 0) {
1312 int32_t placeholderLen = 3; // length of "{0}" or "{1}"
1313 if (index0 < index1) {
1314 if (pos0.getEndIndex() > 0) {
1315 posResult.setBeginIndex(pos0.getBeginIndex() + index0);
1316 posResult.setEndIndex(pos0.getEndIndex() + index0);
1317 } else if (pos1.getEndIndex() > 0) {
1319 index1 += pat0.length() - placeholderLen; // adjust for pat0 replacing {0}
1320 posResult.setBeginIndex(pos1.getBeginIndex() + index1);
1321 posResult.setEndIndex(pos1.getEndIndex() + index1);
1324 if (pos1.getEndIndex() > 0) {
1325 posResult.setBeginIndex(pos1.getBeginIndex() + index1);
1326 posResult.setEndIndex(pos1.getEndIndex() + index1);
1327 } else if (pos0.getEndIndex() > 0) {
1329 index0 += pat1.length() - placeholderLen; // adjust for pat1 replacing {1}
1330 posResult.setBeginIndex(pos0.getBeginIndex() + index0);
1331 posResult.setEndIndex(pos0.getEndIndex() + index0);
1337 DateIntervalFormat::fallbackFormat(Calendar& fromCalendar,
1338 Calendar& toCalendar,
1339 UBool fromToOnSameDay, // new
1340 UnicodeString& appendTo,
1342 UErrorCode& status) const {
1343 if ( U_FAILURE(status) ) {
1346 UnicodeString fullPattern; // for saving the pattern in fDateFormat
1347 UBool formatDatePlusTimeRange = (fromToOnSameDay && fDatePattern && fTimePattern);
1349 if (formatDatePlusTimeRange) {
1350 fDateFormat->toPattern(fullPattern); // save current pattern, restore later
1351 fDateFormat->applyPattern(*fTimePattern);
1353 FieldPosition otherPos;
1354 otherPos.setField(pos.getField());
1355 UnicodeString earlierDate;
1356 fDateFormat->format(fromCalendar, earlierDate, pos);
1357 UnicodeString laterDate;
1358 fDateFormat->format(toCalendar, laterDate, otherPos);
1359 UnicodeString fallbackPattern;
1360 fInfo->getFallbackIntervalPattern(fallbackPattern);
1361 adjustPosition(fallbackPattern, earlierDate, pos, laterDate, otherPos, pos);
1362 UnicodeString fallbackRange;
1363 SimpleFormatter(fallbackPattern, 2, 2, status).
1364 format(earlierDate, laterDate, fallbackRange, status);
1365 if ( U_SUCCESS(status) && formatDatePlusTimeRange ) {
1366 // fallbackRange has just the time range, need to format the date part and combine that
1367 fDateFormat->applyPattern(*fDatePattern);
1368 UnicodeString datePortion;
1369 otherPos.setBeginIndex(0);
1370 otherPos.setEndIndex(0);
1371 fDateFormat->format(fromCalendar, datePortion, otherPos);
1372 adjustPosition(*fDateTimeFormat, fallbackRange, pos, datePortion, otherPos, pos);
1373 const UnicodeString *values[2] = {
1374 &fallbackRange, // {0} is time range
1375 &datePortion, // {1} is single date portion
1377 SimpleFormatter(*fDateTimeFormat, 2, 2, status).
1378 formatAndReplace(values, 2, fallbackRange, NULL, 0, status);
1380 if ( U_SUCCESS(status) ) {
1381 appendTo.append(fallbackRange);
1383 if (formatDatePlusTimeRange) {
1384 // restore full pattern
1385 fDateFormat->applyPattern(fullPattern);
1394 DateIntervalFormat::fieldExistsInSkeleton(UCalendarDateFields field,
1395 const UnicodeString& skeleton)
1397 const UChar fieldChar = fgCalendarFieldToPatternLetter[field];
1398 return ( (skeleton.indexOf(fieldChar) == -1)?FALSE:TRUE ) ;
1404 DateIntervalFormat::adjustFieldWidth(const UnicodeString& inputSkeleton,
1405 const UnicodeString& bestMatchSkeleton,
1406 const UnicodeString& bestIntervalPattern,
1407 int8_t differenceInfo,
1408 UnicodeString& adjustedPtn) {
1409 adjustedPtn = bestIntervalPattern;
1410 int32_t inputSkeletonFieldWidth[] =
1412 // A B C D E F G H I J K L M N O
1413 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
1414 // P Q R S T U V W X Y Z
1415 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
1416 // a b c d e f g h i j k l m n o
1417 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
1418 // p q r s t u v w x y z
1419 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0
1422 int32_t bestMatchSkeletonFieldWidth[] =
1424 // A B C D E F G H I J K L M N O
1425 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
1426 // P Q R S T U V W X Y Z
1427 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
1428 // a b c d e f g h i j k l m n o
1429 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
1430 // p q r s t u v w x y z
1431 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0
1434 DateIntervalInfo::parseSkeleton(inputSkeleton, inputSkeletonFieldWidth);
1435 DateIntervalInfo::parseSkeleton(bestMatchSkeleton, bestMatchSkeletonFieldWidth);
1436 if ( differenceInfo == 2 ) {
1437 adjustedPtn.findAndReplace(UnicodeString((UChar)0x76 /* v */),
1438 UnicodeString((UChar)0x7a /* z */));
1441 UBool inQuote = false;
1445 const int8_t PATTERN_CHAR_BASE = 0x41;
1447 // loop through the pattern string character by character
1448 int32_t adjustedPtnLength = adjustedPtn.length();
1450 for (i = 0; i < adjustedPtnLength; ++i) {
1451 UChar ch = adjustedPtn.charAt(i);
1452 if (ch != prevCh && count > 0) {
1453 // check the repeativeness of pattern letter
1454 UChar skeletonChar = prevCh;
1455 if ( skeletonChar == CAP_L ) {
1456 // there is no "L" (always be "M") in skeleton,
1457 // but there is "L" in pattern.
1458 // for skeleton "M+", the pattern might be "...L..."
1459 skeletonChar = CAP_M;
1461 int32_t fieldCount = bestMatchSkeletonFieldWidth[(int)(skeletonChar - PATTERN_CHAR_BASE)];
1462 int32_t inputFieldCount = inputSkeletonFieldWidth[(int)(skeletonChar - PATTERN_CHAR_BASE)];
1463 if ( fieldCount == count && inputFieldCount > fieldCount ) {
1464 count = inputFieldCount - fieldCount;
1466 for ( j = 0; j < count; ++j ) {
1467 adjustedPtn.insert(i, prevCh);
1470 adjustedPtnLength += count;
1474 if (ch == 0x0027 /*'*/) {
1475 // Consecutive single quotes are a single quote literal,
1476 // either outside of quotes or between quotes
1477 if ((i+1) < adjustedPtn.length() && adjustedPtn.charAt(i+1) == 0x0027 /* ' */) {
1480 inQuote = ! inQuote;
1483 else if ( ! inQuote && ((ch >= 0x0061 /*'a'*/ && ch <= 0x007A /*'z'*/)
1484 || (ch >= 0x0041 /*'A'*/ && ch <= 0x005A /*'Z'*/))) {
1485 // ch is a date-time pattern character
1492 // check the repeativeness of pattern letter
1493 UChar skeletonChar = prevCh;
1494 if ( skeletonChar == CAP_L ) {
1495 // there is no "L" (always be "M") in skeleton,
1496 // but there is "L" in pattern.
1497 // for skeleton "M+", the pattern might be "...L..."
1498 skeletonChar = CAP_M;
1500 int32_t fieldCount = bestMatchSkeletonFieldWidth[(int)(skeletonChar - PATTERN_CHAR_BASE)];
1501 int32_t inputFieldCount = inputSkeletonFieldWidth[(int)(skeletonChar - PATTERN_CHAR_BASE)];
1502 if ( fieldCount == count && inputFieldCount > fieldCount ) {
1503 count = inputFieldCount - fieldCount;
1505 for ( j = 0; j < count; ++j ) {
1506 adjustedPtn.append(prevCh);
1515 DateIntervalFormat::concatSingleDate2TimeInterval(UnicodeString& format,
1516 const UnicodeString& datePattern,
1517 UCalendarDateFields field,
1518 UErrorCode& status) {
1519 // following should not set wrong status
1520 int32_t itvPtnIndex = DateIntervalInfo::calendarFieldToIntervalIndex(field,
1522 if ( U_FAILURE(status) ) {
1525 PatternInfo& timeItvPtnInfo = fIntervalPatterns[itvPtnIndex];
1526 if ( !timeItvPtnInfo.firstPart.isEmpty() ) {
1527 UnicodeString timeIntervalPattern(timeItvPtnInfo.firstPart);
1528 timeIntervalPattern.append(timeItvPtnInfo.secondPart);
1529 UnicodeString combinedPattern;
1530 SimpleFormatter(format, 2, 2, status).
1531 format(timeIntervalPattern, datePattern, combinedPattern, status);
1532 if ( U_FAILURE(status) ) {
1535 setIntervalPattern(field, combinedPattern, timeItvPtnInfo.laterDateFirst);
1538 // it should not happen if the interval format defined is valid
1544 DateIntervalFormat::fgCalendarFieldToPatternLetter[] =
1546 /*GyM*/ CAP_G, LOW_Y, CAP_M,
1547 /*wWd*/ LOW_W, CAP_W, LOW_D,
1548 /*DEF*/ CAP_D, CAP_E, CAP_F,
1549 /*ahH*/ LOW_A, LOW_H, CAP_H,
1550 /*msS*/ LOW_M, LOW_S, CAP_S, // MINUTE, SECOND, MILLISECOND
1551 /*z.Y*/ LOW_Z, SPACE, CAP_Y, // ZONE_OFFSET, DST_OFFSET, YEAR_WOY,
1552 /*eug*/ LOW_E, LOW_U, LOW_G, // DOW_LOCAL, EXTENDED_YEAR, JULIAN_DAY,
1553 /*A..*/ CAP_A, SPACE, SPACE, // MILLISECONDS_IN_DAY, IS_LEAP_MONTH, FIELD_COUNT