• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 // © 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 *******************************************************************************
7 *
8 * File DTITVFMT.CPP
9 *
10 *******************************************************************************
11 */
12 
13 #include "utypeinfo.h"  // for 'typeid' to work
14 
15 #include "unicode/dtitvfmt.h"
16 
17 #if !UCONFIG_NO_FORMATTING
18 
19 //TODO: put in compilation
20 //#define DTITVFMT_DEBUG 1
21 
22 #include "unicode/calendar.h"
23 #include "unicode/dtptngen.h"
24 #include "unicode/dtitvinf.h"
25 #include "unicode/simpleformatter.h"
26 #include "cmemory.h"
27 #include "cstring.h"
28 #include "dtitv_impl.h"
29 #include "mutex.h"
30 #include "uresimp.h"
31 #include "formattedval_impl.h"
32 
33 #ifdef DTITVFMT_DEBUG
34 #include <iostream>
35 #endif
36 
37 U_NAMESPACE_BEGIN
38 
39 
40 
41 #ifdef DTITVFMT_DEBUG
42 #define PRINTMESG(msg) { std::cout << "(" << __FILE__ << ":" << __LINE__ << ") " << msg << "\n"; }
43 #endif
44 
45 
46 static const UChar gDateFormatSkeleton[][11] = {
47 //yMMMMEEEEd
48 {LOW_Y, CAP_M, CAP_M, CAP_M, CAP_M, CAP_E, CAP_E, CAP_E, CAP_E, LOW_D, 0},
49 //yMMMMd
50 {LOW_Y, CAP_M, CAP_M, CAP_M, CAP_M, LOW_D, 0},
51 //yMMMd
52 {LOW_Y, CAP_M, CAP_M, CAP_M, LOW_D, 0},
53 //yMd
54 {LOW_Y, CAP_M, LOW_D, 0} };
55 
56 
57 static const char gCalendarTag[] = "calendar";
58 static const char gGregorianTag[] = "gregorian";
59 static const char gDateTimePatternsTag[] = "DateTimePatterns";
60 
61 
62 // latestFirst:
63 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};
64 
65 // earliestFirst:
66 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};
67 
68 
69 class FormattedDateIntervalData : public FormattedValueFieldPositionIteratorImpl {
70 public:
FormattedDateIntervalData(UErrorCode & status)71     FormattedDateIntervalData(UErrorCode& status) : FormattedValueFieldPositionIteratorImpl(5, status) {}
72     virtual ~FormattedDateIntervalData();
73 };
74 
75 FormattedDateIntervalData::~FormattedDateIntervalData() = default;
76 
77 UPRV_FORMATTED_VALUE_SUBCLASS_AUTO_IMPL(FormattedDateInterval)
78 
79 
80 UOBJECT_DEFINE_RTTI_IMPLEMENTATION(DateIntervalFormat)
81 
82 // Mutex, protects access to fDateFormat, fFromCalendar and fToCalendar.
83 //        Needed because these data members are modified by const methods of DateIntervalFormat.
84 
85 static UMutex gFormatterMutex;
86 
87 DateIntervalFormat* U_EXPORT2
createInstance(const UnicodeString & skeleton,UErrorCode & status)88 DateIntervalFormat::createInstance(const UnicodeString& skeleton,
89                                    UErrorCode& status) {
90     return createInstance(skeleton, Locale::getDefault(), status);
91 }
92 
93 
94 DateIntervalFormat* U_EXPORT2
createInstance(const UnicodeString & skeleton,const Locale & locale,UErrorCode & status)95 DateIntervalFormat::createInstance(const UnicodeString& skeleton,
96                                    const Locale& locale,
97                                    UErrorCode& status) {
98 #ifdef DTITVFMT_DEBUG
99     char result[1000];
100     char result_1[1000];
101     char mesg[2000];
102     skeleton.extract(0,  skeleton.length(), result, "UTF-8");
103     UnicodeString pat;
104     ((SimpleDateFormat*)dtfmt)->toPattern(pat);
105     pat.extract(0,  pat.length(), result_1, "UTF-8");
106     sprintf(mesg, "skeleton: %s; pattern: %s\n", result, result_1);
107     PRINTMESG(mesg)
108 #endif
109 
110     DateIntervalInfo* dtitvinf = new DateIntervalInfo(locale, status);
111     if (dtitvinf == nullptr) {
112         status = U_MEMORY_ALLOCATION_ERROR;
113         return nullptr;
114     }
115     return create(locale, dtitvinf, &skeleton, status);
116 }
117 
118 
119 
120 DateIntervalFormat* U_EXPORT2
createInstance(const UnicodeString & skeleton,const DateIntervalInfo & dtitvinf,UErrorCode & status)121 DateIntervalFormat::createInstance(const UnicodeString& skeleton,
122                                    const DateIntervalInfo& dtitvinf,
123                                    UErrorCode& status) {
124     return createInstance(skeleton, Locale::getDefault(), dtitvinf, status);
125 }
126 
127 
128 DateIntervalFormat* U_EXPORT2
createInstance(const UnicodeString & skeleton,const Locale & locale,const DateIntervalInfo & dtitvinf,UErrorCode & status)129 DateIntervalFormat::createInstance(const UnicodeString& skeleton,
130                                    const Locale& locale,
131                                    const DateIntervalInfo& dtitvinf,
132                                    UErrorCode& status) {
133     DateIntervalInfo* ptn = dtitvinf.clone();
134     return create(locale, ptn, &skeleton, status);
135 }
136 
137 
DateIntervalFormat()138 DateIntervalFormat::DateIntervalFormat()
139 :   fInfo(nullptr),
140     fDateFormat(nullptr),
141     fFromCalendar(nullptr),
142     fToCalendar(nullptr),
143     fLocale(Locale::getRoot()),
144     fDatePattern(nullptr),
145     fTimePattern(nullptr),
146     fDateTimeFormat(nullptr)
147 {}
148 
149 
DateIntervalFormat(const DateIntervalFormat & itvfmt)150 DateIntervalFormat::DateIntervalFormat(const DateIntervalFormat& itvfmt)
151 :   Format(itvfmt),
152     fInfo(nullptr),
153     fDateFormat(nullptr),
154     fFromCalendar(nullptr),
155     fToCalendar(nullptr),
156     fLocale(itvfmt.fLocale),
157     fDatePattern(nullptr),
158     fTimePattern(nullptr),
159     fDateTimeFormat(nullptr) {
160     *this = itvfmt;
161 }
162 
163 
164 DateIntervalFormat&
operator =(const DateIntervalFormat & itvfmt)165 DateIntervalFormat::operator=(const DateIntervalFormat& itvfmt) {
166     if ( this != &itvfmt ) {
167         delete fDateFormat;
168         delete fInfo;
169         delete fFromCalendar;
170         delete fToCalendar;
171         delete fDatePattern;
172         delete fTimePattern;
173         delete fDateTimeFormat;
174         {
175             Mutex lock(&gFormatterMutex);
176             if ( itvfmt.fDateFormat ) {
177                 fDateFormat = itvfmt.fDateFormat->clone();
178             } else {
179                 fDateFormat = nullptr;
180             }
181             if ( itvfmt.fFromCalendar ) {
182                 fFromCalendar = itvfmt.fFromCalendar->clone();
183             } else {
184                 fFromCalendar = nullptr;
185             }
186             if ( itvfmt.fToCalendar ) {
187                 fToCalendar = itvfmt.fToCalendar->clone();
188             } else {
189                 fToCalendar = nullptr;
190             }
191         }
192         if ( itvfmt.fInfo ) {
193             fInfo = itvfmt.fInfo->clone();
194         } else {
195             fInfo = nullptr;
196         }
197         fSkeleton = itvfmt.fSkeleton;
198         int8_t i;
199         for ( i = 0; i< DateIntervalInfo::kIPI_MAX_INDEX; ++i ) {
200             fIntervalPatterns[i] = itvfmt.fIntervalPatterns[i];
201         }
202         fLocale = itvfmt.fLocale;
203         fDatePattern    = (itvfmt.fDatePattern)?    itvfmt.fDatePattern->clone(): nullptr;
204         fTimePattern    = (itvfmt.fTimePattern)?    itvfmt.fTimePattern->clone(): nullptr;
205         fDateTimeFormat = (itvfmt.fDateTimeFormat)? itvfmt.fDateTimeFormat->clone(): nullptr;
206     }
207     return *this;
208 }
209 
210 
~DateIntervalFormat()211 DateIntervalFormat::~DateIntervalFormat() {
212     delete fInfo;
213     delete fDateFormat;
214     delete fFromCalendar;
215     delete fToCalendar;
216     delete fDatePattern;
217     delete fTimePattern;
218     delete fDateTimeFormat;
219 }
220 
221 
222 DateIntervalFormat*
clone() const223 DateIntervalFormat::clone() const {
224     return new DateIntervalFormat(*this);
225 }
226 
227 
228 UBool
operator ==(const Format & other) const229 DateIntervalFormat::operator==(const Format& other) const {
230     if (typeid(*this) != typeid(other)) {return FALSE;}
231     const DateIntervalFormat* fmt = (DateIntervalFormat*)&other;
232     if (this == fmt) {return TRUE;}
233     if (!Format::operator==(other)) {return FALSE;}
234     if ((fInfo != fmt->fInfo) && (fInfo == nullptr || fmt->fInfo == nullptr)) {return FALSE;}
235     if (fInfo && fmt->fInfo && (*fInfo != *fmt->fInfo )) {return FALSE;}
236     {
237         Mutex lock(&gFormatterMutex);
238         if (fDateFormat != fmt->fDateFormat && (fDateFormat == nullptr || fmt->fDateFormat == nullptr)) {return FALSE;}
239         if (fDateFormat && fmt->fDateFormat && (*fDateFormat != *fmt->fDateFormat)) {return FALSE;}
240     }
241     // note: fFromCalendar and fToCalendar hold no persistent state, and therefore do not participate in operator ==.
242     //       fDateFormat has the master calendar for the DateIntervalFormat.
243     if (fSkeleton != fmt->fSkeleton) {return FALSE;}
244     if (fDatePattern != fmt->fDatePattern && (fDatePattern == nullptr || fmt->fDatePattern == nullptr)) {return FALSE;}
245     if (fDatePattern && fmt->fDatePattern && (*fDatePattern != *fmt->fDatePattern)) {return FALSE;}
246     if (fTimePattern != fmt->fTimePattern && (fTimePattern == nullptr || fmt->fTimePattern == nullptr)) {return FALSE;}
247     if (fTimePattern && fmt->fTimePattern && (*fTimePattern != *fmt->fTimePattern)) {return FALSE;}
248     if (fDateTimeFormat != fmt->fDateTimeFormat && (fDateTimeFormat == nullptr || fmt->fDateTimeFormat == nullptr)) {return FALSE;}
249     if (fDateTimeFormat && fmt->fDateTimeFormat && (*fDateTimeFormat != *fmt->fDateTimeFormat)) {return FALSE;}
250     if (fLocale != fmt->fLocale) {return FALSE;}
251 
252     for (int32_t i = 0; i< DateIntervalInfo::kIPI_MAX_INDEX; ++i ) {
253         if (fIntervalPatterns[i].firstPart != fmt->fIntervalPatterns[i].firstPart) {return FALSE;}
254         if (fIntervalPatterns[i].secondPart != fmt->fIntervalPatterns[i].secondPart ) {return FALSE;}
255         if (fIntervalPatterns[i].laterDateFirst != fmt->fIntervalPatterns[i].laterDateFirst) {return FALSE;}
256     }
257     return TRUE;
258 }
259 
260 
261 UnicodeString&
format(const Formattable & obj,UnicodeString & appendTo,FieldPosition & fieldPosition,UErrorCode & status) const262 DateIntervalFormat::format(const Formattable& obj,
263                            UnicodeString& appendTo,
264                            FieldPosition& fieldPosition,
265                            UErrorCode& status) const {
266     if ( U_FAILURE(status) ) {
267         return appendTo;
268     }
269 
270     if ( obj.getType() == Formattable::kObject ) {
271         const UObject* formatObj = obj.getObject();
272         const DateInterval* interval = dynamic_cast<const DateInterval*>(formatObj);
273         if (interval != nullptr) {
274             return format(interval, appendTo, fieldPosition, status);
275         }
276     }
277     status = U_ILLEGAL_ARGUMENT_ERROR;
278     return appendTo;
279 }
280 
281 
282 UnicodeString&
format(const DateInterval * dtInterval,UnicodeString & appendTo,FieldPosition & fieldPosition,UErrorCode & status) const283 DateIntervalFormat::format(const DateInterval* dtInterval,
284                            UnicodeString& appendTo,
285                            FieldPosition& fieldPosition,
286                            UErrorCode& status) const {
287     if ( U_FAILURE(status) ) {
288         return appendTo;
289     }
290     if (fDateFormat == nullptr || fInfo == nullptr) {
291         status = U_INVALID_STATE_ERROR;
292         return appendTo;
293     }
294 
295     FieldPositionOnlyHandler handler(fieldPosition);
296     handler.setAcceptFirstOnly(TRUE);
297     int8_t ignore;
298 
299     Mutex lock(&gFormatterMutex);
300     return formatIntervalImpl(*dtInterval, appendTo, ignore, handler, status);
301 }
302 
303 
formatToValue(const DateInterval & dtInterval,UErrorCode & status) const304 FormattedDateInterval DateIntervalFormat::formatToValue(
305         const DateInterval& dtInterval,
306         UErrorCode& status) const {
307     if (U_FAILURE(status)) {
308         return FormattedDateInterval(status);
309     }
310     // LocalPointer only sets OOM status if U_SUCCESS is true.
311     LocalPointer<FormattedDateIntervalData> result(new FormattedDateIntervalData(status), status);
312     if (U_FAILURE(status)) {
313         return FormattedDateInterval(status);
314     }
315     UnicodeString string;
316     int8_t firstIndex;
317     auto handler = result->getHandler(status);
318     handler.setCategory(UFIELD_CATEGORY_DATE);
319     {
320         Mutex lock(&gFormatterMutex);
321         formatIntervalImpl(dtInterval, string, firstIndex, handler, status);
322     }
323     handler.getError(status);
324     result->appendString(string, status);
325     if (U_FAILURE(status)) {
326         return FormattedDateInterval(status);
327     }
328 
329     // Compute the span fields and sort them into place:
330     if (firstIndex != -1) {
331         result->addOverlapSpans(UFIELD_CATEGORY_DATE_INTERVAL_SPAN, firstIndex, status);
332         if (U_FAILURE(status)) {
333             return FormattedDateInterval(status);
334         }
335         result->sort();
336     }
337 
338     return FormattedDateInterval(result.orphan());
339 }
340 
341 
342 UnicodeString&
format(Calendar & fromCalendar,Calendar & toCalendar,UnicodeString & appendTo,FieldPosition & pos,UErrorCode & status) const343 DateIntervalFormat::format(Calendar& fromCalendar,
344                            Calendar& toCalendar,
345                            UnicodeString& appendTo,
346                            FieldPosition& pos,
347                            UErrorCode& status) const {
348     FieldPositionOnlyHandler handler(pos);
349     handler.setAcceptFirstOnly(TRUE);
350     int8_t ignore;
351 
352     Mutex lock(&gFormatterMutex);
353     return formatImpl(fromCalendar, toCalendar, appendTo, ignore, handler, status);
354 }
355 
356 
formatToValue(Calendar & fromCalendar,Calendar & toCalendar,UErrorCode & status) const357 FormattedDateInterval DateIntervalFormat::formatToValue(
358         Calendar& fromCalendar,
359         Calendar& toCalendar,
360         UErrorCode& status) const {
361     if (U_FAILURE(status)) {
362         return FormattedDateInterval(status);
363     }
364     // LocalPointer only sets OOM status if U_SUCCESS is true.
365     LocalPointer<FormattedDateIntervalData> result(new FormattedDateIntervalData(status), status);
366     if (U_FAILURE(status)) {
367         return FormattedDateInterval(status);
368     }
369     UnicodeString string;
370     int8_t firstIndex;
371     auto handler = result->getHandler(status);
372     handler.setCategory(UFIELD_CATEGORY_DATE);
373     {
374         Mutex lock(&gFormatterMutex);
375         formatImpl(fromCalendar, toCalendar, string, firstIndex, handler, status);
376     }
377     handler.getError(status);
378     result->appendString(string, status);
379     if (U_FAILURE(status)) {
380         return FormattedDateInterval(status);
381     }
382 
383     // Compute the span fields and sort them into place:
384     if (firstIndex != -1) {
385         result->addOverlapSpans(UFIELD_CATEGORY_DATE_INTERVAL_SPAN, firstIndex, status);
386         result->sort();
387     }
388 
389     return FormattedDateInterval(result.orphan());
390 }
391 
392 
formatIntervalImpl(const DateInterval & dtInterval,UnicodeString & appendTo,int8_t & firstIndex,FieldPositionHandler & fphandler,UErrorCode & status) const393 UnicodeString& DateIntervalFormat::formatIntervalImpl(
394         const DateInterval& dtInterval,
395         UnicodeString& appendTo,
396         int8_t& firstIndex,
397         FieldPositionHandler& fphandler,
398         UErrorCode& status) const {
399     if (U_FAILURE(status)) {
400         return appendTo;
401     }
402     if (fFromCalendar == nullptr || fToCalendar == nullptr) {
403         status = U_INVALID_STATE_ERROR;
404         return appendTo;
405     }
406     fFromCalendar->setTime(dtInterval.getFromDate(), status);
407     fToCalendar->setTime(dtInterval.getToDate(), status);
408     return formatImpl(*fFromCalendar, *fToCalendar, appendTo, firstIndex, fphandler, status);
409 }
410 
411 
412 UnicodeString&
formatImpl(Calendar & fromCalendar,Calendar & toCalendar,UnicodeString & appendTo,int8_t & firstIndex,FieldPositionHandler & fphandler,UErrorCode & status) const413 DateIntervalFormat::formatImpl(Calendar& fromCalendar,
414                            Calendar& toCalendar,
415                            UnicodeString& appendTo,
416                            int8_t& firstIndex,
417                            FieldPositionHandler& fphandler,
418                            UErrorCode& status) const {
419     if ( U_FAILURE(status) ) {
420         return appendTo;
421     }
422 
423     // Initialize firstIndex to -1 (single date, no range)
424     firstIndex = -1;
425 
426     // not support different calendar types and time zones
427     //if ( fromCalendar.getType() != toCalendar.getType() ) {
428     if ( !fromCalendar.isEquivalentTo(toCalendar) ) {
429         status = U_ILLEGAL_ARGUMENT_ERROR;
430         return appendTo;
431     }
432 
433     // First, find the largest different calendar field.
434     UCalendarDateFields field = UCAL_FIELD_COUNT;
435 
436     if ( fromCalendar.get(UCAL_ERA,status) != toCalendar.get(UCAL_ERA,status)) {
437         field = UCAL_ERA;
438     } else if ( fromCalendar.get(UCAL_YEAR, status) !=
439                 toCalendar.get(UCAL_YEAR, status) ) {
440         field = UCAL_YEAR;
441     } else if ( fromCalendar.get(UCAL_MONTH, status) !=
442                 toCalendar.get(UCAL_MONTH, status) ) {
443         field = UCAL_MONTH;
444     } else if ( fromCalendar.get(UCAL_DATE, status) !=
445                 toCalendar.get(UCAL_DATE, status) ) {
446         field = UCAL_DATE;
447     } else if ( fromCalendar.get(UCAL_AM_PM, status) !=
448                 toCalendar.get(UCAL_AM_PM, status) ) {
449         field = UCAL_AM_PM;
450     } else if ( fromCalendar.get(UCAL_HOUR, status) !=
451                 toCalendar.get(UCAL_HOUR, status) ) {
452         field = UCAL_HOUR;
453     } else if ( fromCalendar.get(UCAL_MINUTE, status) !=
454                 toCalendar.get(UCAL_MINUTE, status) ) {
455         field = UCAL_MINUTE;
456     } else if ( fromCalendar.get(UCAL_SECOND, status) !=
457                 toCalendar.get(UCAL_SECOND, status) ) {
458         field = UCAL_SECOND;
459     } else if ( fromCalendar.get(UCAL_MILLISECOND, status) !=
460                 toCalendar.get(UCAL_MILLISECOND, status) ) {
461         field = UCAL_MILLISECOND;
462     }
463 
464     if ( U_FAILURE(status) ) {
465         return appendTo;
466     }
467     if ( field == UCAL_FIELD_COUNT ) {
468         /* ignore the millisecond etc. small fields' difference.
469          * use single date when all the above are the same.
470          */
471         return fDateFormat->_format(fromCalendar, appendTo, fphandler, status);
472     }
473     UBool fromToOnSameDay = (field==UCAL_AM_PM || field==UCAL_HOUR || field==UCAL_MINUTE || field==UCAL_SECOND || field==UCAL_MILLISECOND);
474 
475     // following call should not set wrong status,
476     // all the pass-in fields are valid till here
477     int32_t itvPtnIndex = DateIntervalInfo::calendarFieldToIntervalIndex(field,
478                                                                         status);
479     const PatternInfo& intervalPattern = fIntervalPatterns[itvPtnIndex];
480 
481     if ( intervalPattern.firstPart.isEmpty() &&
482          intervalPattern.secondPart.isEmpty() ) {
483         if ( fDateFormat->isFieldUnitIgnored(field) ) {
484             /* the largest different calendar field is small than
485              * the smallest calendar field in pattern,
486              * return single date format.
487              */
488             return fDateFormat->_format(fromCalendar, appendTo, fphandler, status);
489         }
490         return fallbackFormat(fromCalendar, toCalendar, fromToOnSameDay, appendTo, firstIndex, fphandler, status);
491     }
492     // If the first part in interval pattern is empty,
493     // the 2nd part of it saves the full-pattern used in fall-back.
494     // For a 'real' interval pattern, the first part will never be empty.
495     if ( intervalPattern.firstPart.isEmpty() ) {
496         // fall back
497         UnicodeString originalPattern;
498         fDateFormat->toPattern(originalPattern);
499         fDateFormat->applyPattern(intervalPattern.secondPart);
500         appendTo = fallbackFormat(fromCalendar, toCalendar, fromToOnSameDay, appendTo, firstIndex, fphandler, status);
501         fDateFormat->applyPattern(originalPattern);
502         return appendTo;
503     }
504     Calendar* firstCal;
505     Calendar* secondCal;
506     if ( intervalPattern.laterDateFirst ) {
507         firstCal = &toCalendar;
508         secondCal = &fromCalendar;
509         firstIndex = 1;
510     } else {
511         firstCal = &fromCalendar;
512         secondCal = &toCalendar;
513         firstIndex = 0;
514     }
515     // break the interval pattern into 2 parts,
516     // first part should not be empty,
517     UnicodeString originalPattern;
518     fDateFormat->toPattern(originalPattern);
519     fDateFormat->applyPattern(intervalPattern.firstPart);
520     fDateFormat->_format(*firstCal, appendTo, fphandler, status);
521 
522     if ( !intervalPattern.secondPart.isEmpty() ) {
523         fDateFormat->applyPattern(intervalPattern.secondPart);
524         fDateFormat->_format(*secondCal, appendTo, fphandler, status);
525     }
526     fDateFormat->applyPattern(originalPattern);
527     return appendTo;
528 }
529 
530 
531 
532 void
parseObject(const UnicodeString &,Formattable &,ParsePosition &) const533 DateIntervalFormat::parseObject(const UnicodeString& /* source */,
534                                 Formattable& /* result */,
535                                 ParsePosition& /* parse_pos */) const {
536     // parseObject(const UnicodeString&, Formattable&, UErrorCode&) const
537     // will set status as U_INVALID_FORMAT_ERROR if
538     // parse_pos is still 0
539 }
540 
541 
542 
543 
544 const DateIntervalInfo*
getDateIntervalInfo() const545 DateIntervalFormat::getDateIntervalInfo() const {
546     return fInfo;
547 }
548 
549 
550 void
setDateIntervalInfo(const DateIntervalInfo & newItvPattern,UErrorCode & status)551 DateIntervalFormat::setDateIntervalInfo(const DateIntervalInfo& newItvPattern,
552                                         UErrorCode& status) {
553     delete fInfo;
554     fInfo = new DateIntervalInfo(newItvPattern);
555     if (fInfo == nullptr) {
556         status = U_MEMORY_ALLOCATION_ERROR;
557     }
558 
559     // Delete patterns that get reset by initializePattern
560     delete fDatePattern;
561     fDatePattern = nullptr;
562     delete fTimePattern;
563     fTimePattern = nullptr;
564     delete fDateTimeFormat;
565     fDateTimeFormat = nullptr;
566 
567     if (fDateFormat) {
568         initializePattern(status);
569     }
570 }
571 
572 
573 
574 const DateFormat*
getDateFormat() const575 DateIntervalFormat::getDateFormat() const {
576     return fDateFormat;
577 }
578 
579 
580 void
adoptTimeZone(TimeZone * zone)581 DateIntervalFormat::adoptTimeZone(TimeZone* zone)
582 {
583     if (fDateFormat != nullptr) {
584         fDateFormat->adoptTimeZone(zone);
585     }
586     // The fDateFormat has the master calendar for the DateIntervalFormat and has
587     // ownership of any adopted TimeZone; fFromCalendar and fToCalendar are internal
588     // work clones of that calendar (and should not also be given ownership of the
589     // adopted TimeZone).
590     if (fFromCalendar) {
591         fFromCalendar->setTimeZone(*zone);
592     }
593     if (fToCalendar) {
594         fToCalendar->setTimeZone(*zone);
595     }
596 }
597 
598 void
setTimeZone(const TimeZone & zone)599 DateIntervalFormat::setTimeZone(const TimeZone& zone)
600 {
601     if (fDateFormat != nullptr) {
602         fDateFormat->setTimeZone(zone);
603     }
604     // The fDateFormat has the master calendar for the DateIntervalFormat;
605     // fFromCalendar and fToCalendar are internal work clones of that calendar.
606     if (fFromCalendar) {
607         fFromCalendar->setTimeZone(zone);
608     }
609     if (fToCalendar) {
610         fToCalendar->setTimeZone(zone);
611     }
612 }
613 
614 const TimeZone&
getTimeZone() const615 DateIntervalFormat::getTimeZone() const
616 {
617     if (fDateFormat != nullptr) {
618         Mutex lock(&gFormatterMutex);
619         return fDateFormat->getTimeZone();
620     }
621     // If fDateFormat is nullptr (unexpected), create default timezone.
622     return *(TimeZone::createDefault());
623 }
624 
DateIntervalFormat(const Locale & locale,DateIntervalInfo * dtItvInfo,const UnicodeString * skeleton,UErrorCode & status)625 DateIntervalFormat::DateIntervalFormat(const Locale& locale,
626                                        DateIntervalInfo* dtItvInfo,
627                                        const UnicodeString* skeleton,
628                                        UErrorCode& status)
629 :   fInfo(nullptr),
630     fDateFormat(nullptr),
631     fFromCalendar(nullptr),
632     fToCalendar(nullptr),
633     fLocale(locale),
634     fDatePattern(nullptr),
635     fTimePattern(nullptr),
636     fDateTimeFormat(nullptr)
637 {
638     LocalPointer<DateIntervalInfo> info(dtItvInfo, status);
639     LocalPointer<SimpleDateFormat> dtfmt(static_cast<SimpleDateFormat *>(
640             DateFormat::createInstanceForSkeleton(*skeleton, locale, status)), status);
641     if (U_FAILURE(status)) {
642         return;
643     }
644 
645     if ( skeleton ) {
646         fSkeleton = *skeleton;
647     }
648     fInfo = info.orphan();
649     fDateFormat = dtfmt.orphan();
650     if ( fDateFormat->getCalendar() ) {
651         fFromCalendar = fDateFormat->getCalendar()->clone();
652         fToCalendar = fDateFormat->getCalendar()->clone();
653     }
654     initializePattern(status);
655 }
656 
657 DateIntervalFormat* U_EXPORT2
create(const Locale & locale,DateIntervalInfo * dtitvinf,const UnicodeString * skeleton,UErrorCode & status)658 DateIntervalFormat::create(const Locale& locale,
659                            DateIntervalInfo* dtitvinf,
660                            const UnicodeString* skeleton,
661                            UErrorCode& status) {
662     DateIntervalFormat* f = new DateIntervalFormat(locale, dtitvinf,
663                                                    skeleton, status);
664     if ( f == nullptr ) {
665         status = U_MEMORY_ALLOCATION_ERROR;
666         delete dtitvinf;
667     } else if ( U_FAILURE(status) ) {
668         // safe to delete f, although nothing acutally is saved
669         delete f;
670         f = 0;
671     }
672     return f;
673 }
674 
675 
676 
677 /**
678  * Initialize interval patterns locale to this formatter
679  *
680  * This code is a bit complicated since
681  * 1. the interval patterns saved in resource bundle files are interval
682  *    patterns based on date or time only.
683  *    It does not have interval patterns based on both date and time.
684  *    Interval patterns on both date and time are algorithm generated.
685  *
686  *    For example, it has interval patterns on skeleton "dMy" and "hm",
687  *    but it does not have interval patterns on skeleton "dMyhm".
688  *
689  *    The rule to genearte interval patterns for both date and time skeleton are
690  *    1) when the year, month, or day differs, concatenate the two original
691  *    expressions with a separator between,
692  *    For example, interval pattern from "Jan 10, 2007 10:10 am"
693  *    to "Jan 11, 2007 10:10am" is
694  *    "Jan 10, 2007 10:10 am - Jan 11, 2007 10:10am"
695  *
696  *    2) otherwise, present the date followed by the range expression
697  *    for the time.
698  *    For example, interval pattern from "Jan 10, 2007 10:10 am"
699  *    to "Jan 10, 2007 11:10am" is
700  *    "Jan 10, 2007 10:10 am - 11:10am"
701  *
702  * 2. even a pattern does not request a certion calendar field,
703  *    the interval pattern needs to include such field if such fields are
704  *    different between 2 dates.
705  *    For example, a pattern/skeleton is "hm", but the interval pattern
706  *    includes year, month, and date when year, month, and date differs.
707  *
708  * @param status          output param set to success/failure code on exit
709  * @stable ICU 4.0
710  */
711 void
initializePattern(UErrorCode & status)712 DateIntervalFormat::initializePattern(UErrorCode& status) {
713     if ( U_FAILURE(status) ) {
714         return;
715     }
716     const Locale& locale = fDateFormat->getSmpFmtLocale();
717     if ( fSkeleton.isEmpty() ) {
718         UnicodeString fullPattern;
719         fDateFormat->toPattern(fullPattern);
720 #ifdef DTITVFMT_DEBUG
721     char result[1000];
722     char result_1[1000];
723     char mesg[2000];
724     fSkeleton.extract(0,  fSkeleton.length(), result, "UTF-8");
725     sprintf(mesg, "in getBestSkeleton: fSkeleton: %s; \n", result);
726     PRINTMESG(mesg)
727 #endif
728         // fSkeleton is already set by createDateIntervalInstance()
729         // or by createInstance(UnicodeString skeleton, .... )
730         fSkeleton = DateTimePatternGenerator::staticGetSkeleton(
731                 fullPattern, status);
732         if ( U_FAILURE(status) ) {
733             return;
734         }
735     }
736 
737     // initialize the fIntervalPattern ordering
738     int8_t i;
739     for ( i = 0; i < DateIntervalInfo::kIPI_MAX_INDEX; ++i ) {
740         fIntervalPatterns[i].laterDateFirst = fInfo->getDefaultOrder();
741     }
742 
743     /* Check whether the skeleton is a combination of date and time.
744      * For the complication reason 1 explained above.
745      */
746     UnicodeString dateSkeleton;
747     UnicodeString timeSkeleton;
748     UnicodeString normalizedTimeSkeleton;
749     UnicodeString normalizedDateSkeleton;
750 
751 
752     /* the difference between time skeleton and normalizedTimeSkeleton are:
753      * 1. (Formerly, normalized time skeleton folded 'H' to 'h'; no longer true)
754      * 2. 'a' is omitted in normalized time skeleton.
755      * 3. there is only one appearance for 'h' or 'H', 'm','v', 'z' in normalized
756      *    time skeleton
757      *
758      * The difference between date skeleton and normalizedDateSkeleton are:
759      * 1. both 'y' and 'd' appear only once in normalizeDateSkeleton
760      * 2. 'E' and 'EE' are normalized into 'EEE'
761      * 3. 'MM' is normalized into 'M'
762      */
763     getDateTimeSkeleton(fSkeleton, dateSkeleton, normalizedDateSkeleton,
764                         timeSkeleton, normalizedTimeSkeleton);
765 
766 #ifdef DTITVFMT_DEBUG
767     char result[1000];
768     char result_1[1000];
769     char mesg[2000];
770     fSkeleton.extract(0,  fSkeleton.length(), result, "UTF-8");
771     sprintf(mesg, "in getBestSkeleton: fSkeleton: %s; \n", result);
772     PRINTMESG(mesg)
773 #endif
774 
775     // move this up here since we need it for fallbacks
776     if ( timeSkeleton.length() > 0 && dateSkeleton.length() > 0 ) {
777         // Need the Date/Time pattern for concatenation of the date
778         // with the time interval.
779         // The date/time pattern ( such as {0} {1} ) is saved in
780         // calendar, that is why need to get the CalendarData here.
781         LocalUResourceBundlePointer dateTimePatternsRes(ures_open(nullptr, locale.getBaseName(), &status));
782         ures_getByKey(dateTimePatternsRes.getAlias(), gCalendarTag,
783                       dateTimePatternsRes.getAlias(), &status);
784         ures_getByKeyWithFallback(dateTimePatternsRes.getAlias(), gGregorianTag,
785                                   dateTimePatternsRes.getAlias(), &status);
786         ures_getByKeyWithFallback(dateTimePatternsRes.getAlias(), gDateTimePatternsTag,
787                                   dateTimePatternsRes.getAlias(), &status);
788 
789         int32_t dateTimeFormatLength;
790         const UChar* dateTimeFormat = ures_getStringByIndex(
791                                             dateTimePatternsRes.getAlias(),
792                                             (int32_t)DateFormat::kDateTime,
793                                             &dateTimeFormatLength, &status);
794         if ( U_SUCCESS(status) && dateTimeFormatLength >= 3 ) {
795             fDateTimeFormat = new UnicodeString(dateTimeFormat, dateTimeFormatLength);
796             if (fDateTimeFormat == nullptr) {
797                 status = U_MEMORY_ALLOCATION_ERROR;
798                 return;
799             }
800         }
801     }
802 
803     UBool found = setSeparateDateTimePtn(normalizedDateSkeleton,
804                                          normalizedTimeSkeleton);
805 
806     // for skeletons with seconds, found is false and we enter this block
807     if ( found == false ) {
808         // use fallback
809         // TODO: if user asks "m"(minute), but "d"(day) differ
810         if ( timeSkeleton.length() != 0 ) {
811             if ( dateSkeleton.length() == 0 ) {
812                 // prefix with yMd
813                 timeSkeleton.insert(0, gDateFormatSkeleton[DateFormat::kShort], -1);
814                 UnicodeString pattern = DateFormat::getBestPattern(
815                         locale, timeSkeleton, status);
816                 if ( U_FAILURE(status) ) {
817                     return;
818                 }
819                 // for fall back interval patterns,
820                 // the first part of the pattern is empty,
821                 // the second part of the pattern is the full-pattern
822                 // should be used in fall-back.
823                 setPatternInfo(UCAL_DATE, nullptr, &pattern, fInfo->getDefaultOrder());
824                 setPatternInfo(UCAL_MONTH, nullptr, &pattern, fInfo->getDefaultOrder());
825                 setPatternInfo(UCAL_YEAR, nullptr, &pattern, fInfo->getDefaultOrder());
826             } else {
827                 // TODO: fall back
828             }
829         } else {
830             // TODO: fall back
831         }
832         return;
833     } // end of skeleton not found
834     // interval patterns for skeleton are found in resource
835     if ( timeSkeleton.length() == 0 ) {
836         // done
837     } else if ( dateSkeleton.length() == 0 ) {
838         // prefix with yMd
839         timeSkeleton.insert(0, gDateFormatSkeleton[DateFormat::kShort], -1);
840         UnicodeString pattern = DateFormat::getBestPattern(
841                 locale, timeSkeleton, status);
842         if ( U_FAILURE(status) ) {
843             return;
844         }
845         // for fall back interval patterns,
846         // the first part of the pattern is empty,
847         // the second part of the pattern is the full-pattern
848         // should be used in fall-back.
849         setPatternInfo(UCAL_DATE, nullptr, &pattern, fInfo->getDefaultOrder());
850         setPatternInfo(UCAL_MONTH, nullptr, &pattern, fInfo->getDefaultOrder());
851         setPatternInfo(UCAL_YEAR, nullptr, &pattern, fInfo->getDefaultOrder());
852     } else {
853         /* if both present,
854          * 1) when the year, month, or day differs,
855          * concatenate the two original expressions with a separator between,
856          * 2) otherwise, present the date followed by the
857          * range expression for the time.
858          */
859         /*
860          * 1) when the year, month, or day differs,
861          * concatenate the two original expressions with a separator between,
862          */
863         // if field exists, use fall back
864         UnicodeString skeleton = fSkeleton;
865         if ( !fieldExistsInSkeleton(UCAL_DATE, dateSkeleton) ) {
866             // prefix skeleton with 'd'
867             skeleton.insert(0, LOW_D);
868             setFallbackPattern(UCAL_DATE, skeleton, status);
869         }
870         if ( !fieldExistsInSkeleton(UCAL_MONTH, dateSkeleton) ) {
871             // then prefix skeleton with 'M'
872             skeleton.insert(0, CAP_M);
873             setFallbackPattern(UCAL_MONTH, skeleton, status);
874         }
875         if ( !fieldExistsInSkeleton(UCAL_YEAR, dateSkeleton) ) {
876             // then prefix skeleton with 'y'
877             skeleton.insert(0, LOW_Y);
878             setFallbackPattern(UCAL_YEAR, skeleton, status);
879         }
880 
881         /*
882          * 2) otherwise, present the date followed by the
883          * range expression for the time.
884          */
885 
886         if ( fDateTimeFormat == nullptr ) {
887             // earlier failure getting dateTimeFormat
888             return;
889         }
890 
891         UnicodeString datePattern = DateFormat::getBestPattern(
892                 locale, dateSkeleton, status);
893 
894         concatSingleDate2TimeInterval(*fDateTimeFormat, datePattern, UCAL_AM_PM, status);
895         concatSingleDate2TimeInterval(*fDateTimeFormat, datePattern, UCAL_HOUR, status);
896         concatSingleDate2TimeInterval(*fDateTimeFormat, datePattern, UCAL_MINUTE, status);
897     }
898 }
899 
900 
901 
902 void  U_EXPORT2
getDateTimeSkeleton(const UnicodeString & skeleton,UnicodeString & dateSkeleton,UnicodeString & normalizedDateSkeleton,UnicodeString & timeSkeleton,UnicodeString & normalizedTimeSkeleton)903 DateIntervalFormat::getDateTimeSkeleton(const UnicodeString& skeleton,
904                                         UnicodeString& dateSkeleton,
905                                         UnicodeString& normalizedDateSkeleton,
906                                         UnicodeString& timeSkeleton,
907                                         UnicodeString& normalizedTimeSkeleton) {
908     // dateSkeleton follows the sequence of y*M*E*d*
909     // timeSkeleton follows the sequence of hm*[v|z]?
910     int32_t ECount = 0;
911     int32_t dCount = 0;
912     int32_t MCount = 0;
913     int32_t yCount = 0;
914     int32_t hCount = 0;
915     int32_t HCount = 0;
916     int32_t mCount = 0;
917     int32_t vCount = 0;
918     int32_t zCount = 0;
919     int32_t i;
920 
921     for (i = 0; i < skeleton.length(); ++i) {
922         UChar ch = skeleton[i];
923         switch ( ch ) {
924           case CAP_E:
925             dateSkeleton.append(ch);
926             ++ECount;
927             break;
928           case LOW_D:
929             dateSkeleton.append(ch);
930             ++dCount;
931             break;
932           case CAP_M:
933             dateSkeleton.append(ch);
934             ++MCount;
935             break;
936           case LOW_Y:
937             dateSkeleton.append(ch);
938             ++yCount;
939             break;
940           case CAP_G:
941           case CAP_Y:
942           case LOW_U:
943           case CAP_Q:
944           case LOW_Q:
945           case CAP_L:
946           case LOW_L:
947           case CAP_W:
948           case LOW_W:
949           case CAP_D:
950           case CAP_F:
951           case LOW_G:
952           case LOW_E:
953           case LOW_C:
954           case CAP_U:
955           case LOW_R:
956             normalizedDateSkeleton.append(ch);
957             dateSkeleton.append(ch);
958             break;
959           case LOW_A:
960             // 'a' is implicitly handled
961             timeSkeleton.append(ch);
962             break;
963           case LOW_H:
964             timeSkeleton.append(ch);
965             ++hCount;
966             break;
967           case CAP_H:
968             timeSkeleton.append(ch);
969             ++HCount;
970             break;
971           case LOW_M:
972             timeSkeleton.append(ch);
973             ++mCount;
974             break;
975           case LOW_Z:
976             ++zCount;
977             timeSkeleton.append(ch);
978             break;
979           case LOW_V:
980             ++vCount;
981             timeSkeleton.append(ch);
982             break;
983           case CAP_V:
984           case CAP_Z:
985           case LOW_K:
986           case CAP_K:
987           case LOW_J:
988           case LOW_S:
989           case CAP_S:
990           case CAP_A:
991             timeSkeleton.append(ch);
992             normalizedTimeSkeleton.append(ch);
993             break;
994         }
995     }
996 
997     /* generate normalized form for date*/
998     if ( yCount != 0 ) {
999         for (i = 0; i < yCount; ++i) {
1000             normalizedDateSkeleton.append(LOW_Y);
1001         }
1002     }
1003     if ( MCount != 0 ) {
1004         if ( MCount < 3 ) {
1005             normalizedDateSkeleton.append(CAP_M);
1006         } else {
1007             for ( int32_t j = 0; j < MCount && j < MAX_M_COUNT; ++j) {
1008                  normalizedDateSkeleton.append(CAP_M);
1009             }
1010         }
1011     }
1012     if ( ECount != 0 ) {
1013         if ( ECount <= 3 ) {
1014             normalizedDateSkeleton.append(CAP_E);
1015         } else {
1016             for ( int32_t j = 0; j < ECount && j < MAX_E_COUNT; ++j ) {
1017                  normalizedDateSkeleton.append(CAP_E);
1018             }
1019         }
1020     }
1021     if ( dCount != 0 ) {
1022         normalizedDateSkeleton.append(LOW_D);
1023     }
1024 
1025     /* generate normalized form for time */
1026     if ( HCount != 0 ) {
1027         normalizedTimeSkeleton.append(CAP_H);
1028     }
1029     else if ( hCount != 0 ) {
1030         normalizedTimeSkeleton.append(LOW_H);
1031     }
1032     if ( mCount != 0 ) {
1033         normalizedTimeSkeleton.append(LOW_M);
1034     }
1035     if ( zCount != 0 ) {
1036         normalizedTimeSkeleton.append(LOW_Z);
1037     }
1038     if ( vCount != 0 ) {
1039         normalizedTimeSkeleton.append(LOW_V);
1040     }
1041 }
1042 
1043 
1044 /**
1045  * Generate date or time interval pattern from resource,
1046  * and set them into the interval pattern locale to this formatter.
1047  *
1048  * It needs to handle the following:
1049  * 1. need to adjust field width.
1050  *    For example, the interval patterns saved in DateIntervalInfo
1051  *    includes "dMMMy", but not "dMMMMy".
1052  *    Need to get interval patterns for dMMMMy from dMMMy.
1053  *    Another example, the interval patterns saved in DateIntervalInfo
1054  *    includes "hmv", but not "hmz".
1055  *    Need to get interval patterns for "hmz' from 'hmv'
1056  *
1057  * 2. there might be no pattern for 'y' differ for skeleton "Md",
1058  *    in order to get interval patterns for 'y' differ,
1059  *    need to look for it from skeleton 'yMd'
1060  *
1061  * @param dateSkeleton   normalized date skeleton
1062  * @param timeSkeleton   normalized time skeleton
1063  * @return               whether the resource is found for the skeleton.
1064  *                       TRUE if interval pattern found for the skeleton,
1065  *                       FALSE otherwise.
1066  * @stable ICU 4.0
1067  */
1068 UBool
setSeparateDateTimePtn(const UnicodeString & dateSkeleton,const UnicodeString & timeSkeleton)1069 DateIntervalFormat::setSeparateDateTimePtn(
1070                                  const UnicodeString& dateSkeleton,
1071                                  const UnicodeString& timeSkeleton) {
1072     const UnicodeString* skeleton;
1073     // if both date and time skeleton present,
1074     // the final interval pattern might include time interval patterns
1075     // ( when, am_pm, hour, minute differ ),
1076     // but not date interval patterns ( when year, month, day differ ).
1077     // For year/month/day differ, it falls back to fall-back pattern.
1078     if ( timeSkeleton.length() != 0  ) {
1079         skeleton = &timeSkeleton;
1080     } else {
1081         skeleton = &dateSkeleton;
1082     }
1083 
1084     /* interval patterns for skeleton "dMMMy" (but not "dMMMMy")
1085      * are defined in resource,
1086      * interval patterns for skeleton "dMMMMy" are calculated by
1087      * 1. get the best match skeleton for "dMMMMy", which is "dMMMy"
1088      * 2. get the interval patterns for "dMMMy",
1089      * 3. extend "MMM" to "MMMM" in above interval patterns for "dMMMMy"
1090      * getBestSkeleton() is step 1.
1091      */
1092     // best skeleton, and the difference information
1093     int8_t differenceInfo = 0;
1094     const UnicodeString* bestSkeleton = fInfo->getBestSkeleton(*skeleton,
1095                                                                differenceInfo);
1096     /* best skeleton could be nullptr.
1097        For example: in "ca" resource file,
1098        interval format is defined as following
1099            intervalFormats{
1100                 fallback{"{0} - {1}"}
1101             }
1102        there is no skeletons/interval patterns defined,
1103        and the best skeleton match could be nullptr
1104      */
1105     if ( bestSkeleton == nullptr ) {
1106         return false;
1107     }
1108 
1109     // Set patterns for fallback use, need to do this
1110     // before returning if differenceInfo == -1
1111     UErrorCode status;
1112     if ( dateSkeleton.length() != 0) {
1113         status = U_ZERO_ERROR;
1114         fDatePattern = new UnicodeString(DateFormat::getBestPattern(
1115                 fLocale, dateSkeleton, status));
1116         // no way to report OOM. :(
1117     }
1118     if ( timeSkeleton.length() != 0) {
1119         status = U_ZERO_ERROR;
1120         fTimePattern = new UnicodeString(DateFormat::getBestPattern(
1121                 fLocale, timeSkeleton, status));
1122         // no way to report OOM. :(
1123     }
1124 
1125     // difference:
1126     // 0 means the best matched skeleton is the same as input skeleton
1127     // 1 means the fields are the same, but field width are different
1128     // 2 means the only difference between fields are v/z,
1129     // -1 means there are other fields difference
1130     // (this will happen, for instance, if the supplied skeleton has seconds,
1131     //  but no skeletons in the intervalFormats data do)
1132     if ( differenceInfo == -1 ) {
1133         // skeleton has different fields, not only  v/z difference
1134         return false;
1135     }
1136 
1137     if ( timeSkeleton.length() == 0 ) {
1138         UnicodeString extendedSkeleton;
1139         UnicodeString extendedBestSkeleton;
1140         // only has date skeleton
1141         setIntervalPattern(UCAL_DATE, skeleton, bestSkeleton, differenceInfo,
1142                            &extendedSkeleton, &extendedBestSkeleton);
1143 
1144         UBool extended = setIntervalPattern(UCAL_MONTH, skeleton, bestSkeleton,
1145                                      differenceInfo,
1146                                      &extendedSkeleton, &extendedBestSkeleton);
1147 
1148         if ( extended ) {
1149             bestSkeleton = &extendedBestSkeleton;
1150             skeleton = &extendedSkeleton;
1151         }
1152         setIntervalPattern(UCAL_YEAR, skeleton, bestSkeleton, differenceInfo,
1153                            &extendedSkeleton, &extendedBestSkeleton);
1154         setIntervalPattern(UCAL_ERA, skeleton, bestSkeleton, differenceInfo,
1155                            &extendedSkeleton, &extendedBestSkeleton);
1156      } else {
1157         setIntervalPattern(UCAL_MINUTE, skeleton, bestSkeleton, differenceInfo);
1158         setIntervalPattern(UCAL_HOUR, skeleton, bestSkeleton, differenceInfo);
1159         setIntervalPattern(UCAL_AM_PM, skeleton, bestSkeleton, differenceInfo);
1160     }
1161     return true;
1162 }
1163 
1164 
1165 
1166 void
setFallbackPattern(UCalendarDateFields field,const UnicodeString & skeleton,UErrorCode & status)1167 DateIntervalFormat::setFallbackPattern(UCalendarDateFields field,
1168                                        const UnicodeString& skeleton,
1169                                        UErrorCode& status) {
1170     if ( U_FAILURE(status) ) {
1171         return;
1172     }
1173     UnicodeString pattern = DateFormat::getBestPattern(
1174             fLocale, skeleton, status);
1175     if ( U_FAILURE(status) ) {
1176         return;
1177     }
1178     setPatternInfo(field, nullptr, &pattern, fInfo->getDefaultOrder());
1179 }
1180 
1181 
1182 
1183 
1184 void
setPatternInfo(UCalendarDateFields field,const UnicodeString * firstPart,const UnicodeString * secondPart,UBool laterDateFirst)1185 DateIntervalFormat::setPatternInfo(UCalendarDateFields field,
1186                                    const UnicodeString* firstPart,
1187                                    const UnicodeString* secondPart,
1188                                    UBool laterDateFirst) {
1189     // for fall back interval patterns,
1190     // the first part of the pattern is empty,
1191     // the second part of the pattern is the full-pattern
1192     // should be used in fall-back.
1193     UErrorCode status = U_ZERO_ERROR;
1194     // following should not set any wrong status.
1195     int32_t itvPtnIndex = DateIntervalInfo::calendarFieldToIntervalIndex(field,
1196                                                                         status);
1197     if ( U_FAILURE(status) ) {
1198         return;
1199     }
1200     PatternInfo& ptn = fIntervalPatterns[itvPtnIndex];
1201     if ( firstPart ) {
1202         ptn.firstPart = *firstPart;
1203     }
1204     if ( secondPart ) {
1205         ptn.secondPart = *secondPart;
1206     }
1207     ptn.laterDateFirst = laterDateFirst;
1208 }
1209 
1210 void
setIntervalPattern(UCalendarDateFields field,const UnicodeString & intervalPattern)1211 DateIntervalFormat::setIntervalPattern(UCalendarDateFields field,
1212                                        const UnicodeString& intervalPattern) {
1213     UBool order = fInfo->getDefaultOrder();
1214     setIntervalPattern(field, intervalPattern, order);
1215 }
1216 
1217 
1218 void
setIntervalPattern(UCalendarDateFields field,const UnicodeString & intervalPattern,UBool laterDateFirst)1219 DateIntervalFormat::setIntervalPattern(UCalendarDateFields field,
1220                                        const UnicodeString& intervalPattern,
1221                                        UBool laterDateFirst) {
1222     const UnicodeString* pattern = &intervalPattern;
1223     UBool order = laterDateFirst;
1224     // check for "latestFirst:" or "earliestFirst:" prefix
1225     int8_t prefixLength = UPRV_LENGTHOF(gLaterFirstPrefix);
1226     int8_t earliestFirstLength = UPRV_LENGTHOF(gEarlierFirstPrefix);
1227     UnicodeString realPattern;
1228     if ( intervalPattern.startsWith(gLaterFirstPrefix, prefixLength) ) {
1229         order = true;
1230         intervalPattern.extract(prefixLength,
1231                                 intervalPattern.length() - prefixLength,
1232                                 realPattern);
1233         pattern = &realPattern;
1234     } else if ( intervalPattern.startsWith(gEarlierFirstPrefix,
1235                                            earliestFirstLength) ) {
1236         order = false;
1237         intervalPattern.extract(earliestFirstLength,
1238                                 intervalPattern.length() - earliestFirstLength,
1239                                 realPattern);
1240         pattern = &realPattern;
1241     }
1242 
1243     int32_t splitPoint = splitPatternInto2Part(*pattern);
1244 
1245     UnicodeString firstPart;
1246     UnicodeString secondPart;
1247     pattern->extract(0, splitPoint, firstPart);
1248     if ( splitPoint < pattern->length() ) {
1249         pattern->extract(splitPoint, pattern->length()-splitPoint, secondPart);
1250     }
1251     setPatternInfo(field, &firstPart, &secondPart, order);
1252 }
1253 
1254 
1255 
1256 
1257 /**
1258  * Generate interval pattern from existing resource
1259  *
1260  * It not only save the interval patterns,
1261  * but also return the extended skeleton and its best match skeleton.
1262  *
1263  * @param field           largest different calendar field
1264  * @param skeleton        skeleton
1265  * @param bestSkeleton    the best match skeleton which has interval pattern
1266  *                        defined in resource
1267  * @param differenceInfo  the difference between skeleton and best skeleton
1268  *         0 means the best matched skeleton is the same as input skeleton
1269  *         1 means the fields are the same, but field width are different
1270  *         2 means the only difference between fields are v/z,
1271  *        -1 means there are other fields difference
1272  *
1273  * @param extendedSkeleton      extended skeleton
1274  * @param extendedBestSkeleton  extended best match skeleton
1275  * @return                      whether the interval pattern is found
1276  *                              through extending skeleton or not.
1277  *                              TRUE if interval pattern is found by
1278  *                              extending skeleton, FALSE otherwise.
1279  * @stable ICU 4.0
1280  */
1281 UBool
setIntervalPattern(UCalendarDateFields field,const UnicodeString * skeleton,const UnicodeString * bestSkeleton,int8_t differenceInfo,UnicodeString * extendedSkeleton,UnicodeString * extendedBestSkeleton)1282 DateIntervalFormat::setIntervalPattern(UCalendarDateFields field,
1283                                        const UnicodeString* skeleton,
1284                                        const UnicodeString* bestSkeleton,
1285                                        int8_t differenceInfo,
1286                                        UnicodeString* extendedSkeleton,
1287                                        UnicodeString* extendedBestSkeleton) {
1288     UErrorCode status = U_ZERO_ERROR;
1289     // following getIntervalPattern() should not generate error status
1290     UnicodeString pattern;
1291     fInfo->getIntervalPattern(*bestSkeleton, field, pattern, status);
1292     if ( pattern.isEmpty() ) {
1293         // single date
1294         if ( SimpleDateFormat::isFieldUnitIgnored(*bestSkeleton, field) ) {
1295             // do nothing, format will handle it
1296             return false;
1297         }
1298 
1299         // for 24 hour system, interval patterns in resource file
1300         // might not include pattern when am_pm differ,
1301         // which should be the same as hour differ.
1302         // add it here for simplicity
1303         if ( field == UCAL_AM_PM ) {
1304             fInfo->getIntervalPattern(*bestSkeleton, UCAL_HOUR, pattern,status);
1305             if ( !pattern.isEmpty() ) {
1306                 setIntervalPattern(field, pattern);
1307             }
1308             return false;
1309         }
1310         // else, looking for pattern when 'y' differ for 'dMMMM' skeleton,
1311         // first, get best match pattern "MMMd",
1312         // since there is no pattern for 'y' differs for skeleton 'MMMd',
1313         // need to look for it from skeleton 'yMMMd',
1314         // if found, adjust field width in interval pattern from
1315         // "MMM" to "MMMM".
1316         UChar fieldLetter = fgCalendarFieldToPatternLetter[field];
1317         if ( extendedSkeleton ) {
1318             *extendedSkeleton = *skeleton;
1319             *extendedBestSkeleton = *bestSkeleton;
1320             extendedSkeleton->insert(0, fieldLetter);
1321             extendedBestSkeleton->insert(0, fieldLetter);
1322             // for example, looking for patterns when 'y' differ for
1323             // skeleton "MMMM".
1324             fInfo->getIntervalPattern(*extendedBestSkeleton,field,pattern,status);
1325             if ( pattern.isEmpty() && differenceInfo == 0 ) {
1326                 // if there is no skeleton "yMMMM" defined,
1327                 // look for the best match skeleton, for example: "yMMM"
1328                 const UnicodeString* tmpBest = fInfo->getBestSkeleton(
1329                                         *extendedBestSkeleton, differenceInfo);
1330                 if ( tmpBest != 0 && differenceInfo != -1 ) {
1331                     fInfo->getIntervalPattern(*tmpBest, field, pattern, status);
1332                     bestSkeleton = tmpBest;
1333                 }
1334             }
1335         }
1336     }
1337     if ( !pattern.isEmpty() ) {
1338         if ( differenceInfo != 0 ) {
1339             UnicodeString adjustIntervalPattern;
1340             adjustFieldWidth(*skeleton, *bestSkeleton, pattern, differenceInfo,
1341                               adjustIntervalPattern);
1342             setIntervalPattern(field, adjustIntervalPattern);
1343         } else {
1344             setIntervalPattern(field, pattern);
1345         }
1346         if ( extendedSkeleton && !extendedSkeleton->isEmpty() ) {
1347             return TRUE;
1348         }
1349     }
1350     return FALSE;
1351 }
1352 
1353 
1354 
1355 int32_t  U_EXPORT2
splitPatternInto2Part(const UnicodeString & intervalPattern)1356 DateIntervalFormat::splitPatternInto2Part(const UnicodeString& intervalPattern) {
1357     UBool inQuote = false;
1358     UChar prevCh = 0;
1359     int32_t count = 0;
1360 
1361     /* repeatedPattern used to record whether a pattern has already seen.
1362        It is a pattern applies to first calendar if it is first time seen,
1363        otherwise, it is a pattern applies to the second calendar
1364      */
1365     UBool patternRepeated[] =
1366     {
1367     //       A   B   C   D   E   F   G   H   I   J   K   L   M   N   O
1368              0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,
1369     //   P   Q   R   S   T   U   V   W   X   Y   Z
1370          0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0, 0, 0,  0, 0, 0,
1371     //       a   b   c   d   e   f   g   h   i   j   k   l   m   n   o
1372          0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,
1373     //   p   q   r   s   t   u   v   w   x   y   z
1374          0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0
1375     };
1376 
1377     int8_t PATTERN_CHAR_BASE = 0x41;
1378 
1379     /* loop through the pattern string character by character looking for
1380      * the first repeated pattern letter, which breaks the interval pattern
1381      * into 2 parts.
1382      */
1383     int32_t i;
1384     UBool foundRepetition = false;
1385     for (i = 0; i < intervalPattern.length(); ++i) {
1386         UChar ch = intervalPattern.charAt(i);
1387 
1388         if (ch != prevCh && count > 0) {
1389             // check the repeativeness of pattern letter
1390             UBool repeated = patternRepeated[(int)(prevCh - PATTERN_CHAR_BASE)];
1391             if ( repeated == FALSE ) {
1392                 patternRepeated[prevCh - PATTERN_CHAR_BASE] = TRUE;
1393             } else {
1394                 foundRepetition = true;
1395                 break;
1396             }
1397             count = 0;
1398         }
1399         if (ch == 0x0027 /*'*/) {
1400             // Consecutive single quotes are a single quote literal,
1401             // either outside of quotes or between quotes
1402             if ((i+1) < intervalPattern.length() &&
1403                 intervalPattern.charAt(i+1) == 0x0027 /*'*/) {
1404                 ++i;
1405             } else {
1406                 inQuote = ! inQuote;
1407             }
1408         }
1409         else if (!inQuote && ((ch >= 0x0061 /*'a'*/ && ch <= 0x007A /*'z'*/)
1410                     || (ch >= 0x0041 /*'A'*/ && ch <= 0x005A /*'Z'*/))) {
1411             // ch is a date-time pattern character
1412             prevCh = ch;
1413             ++count;
1414         }
1415     }
1416     // check last pattern char, distinguish
1417     // "dd MM" ( no repetition ),
1418     // "d-d"(last char repeated ), and
1419     // "d-d MM" ( repetition found )
1420     if ( count > 0 && foundRepetition == FALSE ) {
1421         if ( patternRepeated[(int)(prevCh - PATTERN_CHAR_BASE)] == FALSE ) {
1422             count = 0;
1423         }
1424     }
1425     return (i - count);
1426 }
1427 
fallbackFormatRange(Calendar & fromCalendar,Calendar & toCalendar,UnicodeString & appendTo,int8_t & firstIndex,FieldPositionHandler & fphandler,UErrorCode & status) const1428 void DateIntervalFormat::fallbackFormatRange(
1429         Calendar& fromCalendar,
1430         Calendar& toCalendar,
1431         UnicodeString& appendTo,
1432         int8_t& firstIndex,
1433         FieldPositionHandler& fphandler,
1434         UErrorCode& status) const {
1435     UnicodeString fallbackPattern;
1436     fInfo->getFallbackIntervalPattern(fallbackPattern);
1437     SimpleFormatter sf(fallbackPattern, 2, 2, status);
1438     if (U_FAILURE(status)) {
1439         return;
1440     }
1441     int32_t offsets[2];
1442     UnicodeString patternBody = sf.getTextWithNoArguments(offsets, 2);
1443 
1444     // TODO(ICU-20406): Use SimpleFormatter Iterator interface when available.
1445     if (offsets[0] < offsets[1]) {
1446         firstIndex = 0;
1447         appendTo.append(patternBody.tempSubStringBetween(0, offsets[0]));
1448         fDateFormat->_format(fromCalendar, appendTo, fphandler, status);
1449         appendTo.append(patternBody.tempSubStringBetween(offsets[0], offsets[1]));
1450         fDateFormat->_format(toCalendar, appendTo, fphandler, status);
1451         appendTo.append(patternBody.tempSubStringBetween(offsets[1]));
1452     } else {
1453         firstIndex = 1;
1454         appendTo.append(patternBody.tempSubStringBetween(0, offsets[1]));
1455         fDateFormat->_format(toCalendar, appendTo, fphandler, status);
1456         appendTo.append(patternBody.tempSubStringBetween(offsets[1], offsets[0]));
1457         fDateFormat->_format(fromCalendar, appendTo, fphandler, status);
1458         appendTo.append(patternBody.tempSubStringBetween(offsets[0]));
1459     }
1460 }
1461 
1462 UnicodeString&
fallbackFormat(Calendar & fromCalendar,Calendar & toCalendar,UBool fromToOnSameDay,UnicodeString & appendTo,int8_t & firstIndex,FieldPositionHandler & fphandler,UErrorCode & status) const1463 DateIntervalFormat::fallbackFormat(Calendar& fromCalendar,
1464                                    Calendar& toCalendar,
1465                                    UBool fromToOnSameDay, // new
1466                                    UnicodeString& appendTo,
1467                                    int8_t& firstIndex,
1468                                    FieldPositionHandler& fphandler,
1469                                    UErrorCode& status) const {
1470     if ( U_FAILURE(status) ) {
1471         return appendTo;
1472     }
1473 
1474     UBool formatDatePlusTimeRange = (fromToOnSameDay && fDatePattern && fTimePattern);
1475     if (formatDatePlusTimeRange) {
1476         SimpleFormatter sf(*fDateTimeFormat, 2, 2, status);
1477         if (U_FAILURE(status)) {
1478             return appendTo;
1479         }
1480         int32_t offsets[2];
1481         UnicodeString patternBody = sf.getTextWithNoArguments(offsets, 2);
1482 
1483         UnicodeString fullPattern; // for saving the pattern in fDateFormat
1484         fDateFormat->toPattern(fullPattern); // save current pattern, restore later
1485 
1486         // {0} is time range
1487         // {1} is single date portion
1488         // TODO(ICU-20406): Use SimpleFormatter Iterator interface when available.
1489         if (offsets[0] < offsets[1]) {
1490             appendTo.append(patternBody.tempSubStringBetween(0, offsets[0]));
1491             fDateFormat->applyPattern(*fTimePattern);
1492             fallbackFormatRange(fromCalendar, toCalendar, appendTo, firstIndex, fphandler, status);
1493             appendTo.append(patternBody.tempSubStringBetween(offsets[0], offsets[1]));
1494             fDateFormat->applyPattern(*fDatePattern);
1495             fDateFormat->_format(fromCalendar, appendTo, fphandler, status);
1496             appendTo.append(patternBody.tempSubStringBetween(offsets[1]));
1497         } else {
1498             appendTo.append(patternBody.tempSubStringBetween(0, offsets[1]));
1499             fDateFormat->applyPattern(*fDatePattern);
1500             fDateFormat->_format(fromCalendar, appendTo, fphandler, status);
1501             appendTo.append(patternBody.tempSubStringBetween(offsets[1], offsets[0]));
1502             fDateFormat->applyPattern(*fTimePattern);
1503             fallbackFormatRange(fromCalendar, toCalendar, appendTo, firstIndex, fphandler, status);
1504             appendTo.append(patternBody.tempSubStringBetween(offsets[0]));
1505         }
1506 
1507         // restore full pattern
1508         fDateFormat->applyPattern(fullPattern);
1509     } else {
1510         fallbackFormatRange(fromCalendar, toCalendar, appendTo, firstIndex, fphandler, status);
1511     }
1512     return appendTo;
1513 }
1514 
1515 
1516 
1517 
1518 UBool  U_EXPORT2
fieldExistsInSkeleton(UCalendarDateFields field,const UnicodeString & skeleton)1519 DateIntervalFormat::fieldExistsInSkeleton(UCalendarDateFields field,
1520                                           const UnicodeString& skeleton)
1521 {
1522     const UChar fieldChar = fgCalendarFieldToPatternLetter[field];
1523     return ( (skeleton.indexOf(fieldChar) == -1)?FALSE:TRUE ) ;
1524 }
1525 
1526 
1527 
1528 void  U_EXPORT2
adjustFieldWidth(const UnicodeString & inputSkeleton,const UnicodeString & bestMatchSkeleton,const UnicodeString & bestIntervalPattern,int8_t differenceInfo,UnicodeString & adjustedPtn)1529 DateIntervalFormat::adjustFieldWidth(const UnicodeString& inputSkeleton,
1530                  const UnicodeString& bestMatchSkeleton,
1531                  const UnicodeString& bestIntervalPattern,
1532                  int8_t differenceInfo,
1533                  UnicodeString& adjustedPtn) {
1534     adjustedPtn = bestIntervalPattern;
1535     int32_t inputSkeletonFieldWidth[] =
1536     {
1537     //       A   B   C   D   E   F   G   H   I   J   K   L   M   N   O
1538              0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,
1539     //   P   Q   R   S   T   U   V   W   X   Y   Z
1540          0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0, 0, 0,  0, 0, 0,
1541     //       a   b   c   d   e   f   g   h   i   j   k   l   m   n   o
1542          0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,
1543     //   p   q   r   s   t   u   v   w   x   y   z
1544          0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0
1545     };
1546 
1547     int32_t bestMatchSkeletonFieldWidth[] =
1548     {
1549     //       A   B   C   D   E   F   G   H   I   J   K   L   M   N   O
1550              0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,
1551     //   P   Q   R   S   T   U   V   W   X   Y   Z
1552          0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0, 0, 0,  0, 0, 0,
1553     //       a   b   c   d   e   f   g   h   i   j   k   l   m   n   o
1554          0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,
1555     //   p   q   r   s   t   u   v   w   x   y   z
1556          0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0
1557     };
1558 
1559     DateIntervalInfo::parseSkeleton(inputSkeleton, inputSkeletonFieldWidth);
1560     DateIntervalInfo::parseSkeleton(bestMatchSkeleton, bestMatchSkeletonFieldWidth);
1561     if ( differenceInfo == 2 ) {
1562         adjustedPtn.findAndReplace(UnicodeString((UChar)0x76 /* v */),
1563                                    UnicodeString((UChar)0x7a /* z */));
1564     }
1565 
1566     UBool inQuote = false;
1567     UChar prevCh = 0;
1568     int32_t count = 0;
1569 
1570     const int8_t PATTERN_CHAR_BASE = 0x41;
1571 
1572     // loop through the pattern string character by character
1573     int32_t adjustedPtnLength = adjustedPtn.length();
1574     int32_t i;
1575     for (i = 0; i < adjustedPtnLength; ++i) {
1576         UChar ch = adjustedPtn.charAt(i);
1577         if (ch != prevCh && count > 0) {
1578             // check the repeativeness of pattern letter
1579             UChar skeletonChar = prevCh;
1580             if ( skeletonChar ==  CAP_L ) {
1581                 // there is no "L" (always be "M") in skeleton,
1582                 // but there is "L" in pattern.
1583                 // for skeleton "M+", the pattern might be "...L..."
1584                 skeletonChar = CAP_M;
1585             }
1586             int32_t fieldCount = bestMatchSkeletonFieldWidth[(int)(skeletonChar - PATTERN_CHAR_BASE)];
1587             int32_t inputFieldCount = inputSkeletonFieldWidth[(int)(skeletonChar - PATTERN_CHAR_BASE)];
1588             if ( fieldCount == count && inputFieldCount > fieldCount ) {
1589                 count = inputFieldCount - fieldCount;
1590                 int32_t j;
1591                 for ( j = 0; j < count; ++j ) {
1592                     adjustedPtn.insert(i, prevCh);
1593                 }
1594                 i += count;
1595                 adjustedPtnLength += count;
1596             }
1597             count = 0;
1598         }
1599         if (ch == 0x0027 /*'*/) {
1600             // Consecutive single quotes are a single quote literal,
1601             // either outside of quotes or between quotes
1602             if ((i+1) < adjustedPtn.length() && adjustedPtn.charAt(i+1) == 0x0027 /* ' */) {
1603                 ++i;
1604             } else {
1605                 inQuote = ! inQuote;
1606             }
1607         }
1608         else if ( ! inQuote && ((ch >= 0x0061 /*'a'*/ && ch <= 0x007A /*'z'*/)
1609                     || (ch >= 0x0041 /*'A'*/ && ch <= 0x005A /*'Z'*/))) {
1610             // ch is a date-time pattern character
1611             prevCh = ch;
1612             ++count;
1613         }
1614     }
1615     if ( count > 0 ) {
1616         // last item
1617         // check the repeativeness of pattern letter
1618         UChar skeletonChar = prevCh;
1619         if ( skeletonChar == CAP_L ) {
1620             // there is no "L" (always be "M") in skeleton,
1621             // but there is "L" in pattern.
1622             // for skeleton "M+", the pattern might be "...L..."
1623             skeletonChar = CAP_M;
1624         }
1625         int32_t fieldCount = bestMatchSkeletonFieldWidth[(int)(skeletonChar - PATTERN_CHAR_BASE)];
1626         int32_t inputFieldCount = inputSkeletonFieldWidth[(int)(skeletonChar - PATTERN_CHAR_BASE)];
1627         if ( fieldCount == count && inputFieldCount > fieldCount ) {
1628             count = inputFieldCount - fieldCount;
1629             int32_t j;
1630             for ( j = 0; j < count; ++j ) {
1631                 adjustedPtn.append(prevCh);
1632             }
1633         }
1634     }
1635 }
1636 
1637 
1638 
1639 void
concatSingleDate2TimeInterval(UnicodeString & format,const UnicodeString & datePattern,UCalendarDateFields field,UErrorCode & status)1640 DateIntervalFormat::concatSingleDate2TimeInterval(UnicodeString& format,
1641                                               const UnicodeString& datePattern,
1642                                               UCalendarDateFields field,
1643                                               UErrorCode& status) {
1644     // following should not set wrong status
1645     int32_t itvPtnIndex = DateIntervalInfo::calendarFieldToIntervalIndex(field,
1646                                                                         status);
1647     if ( U_FAILURE(status) ) {
1648         return;
1649     }
1650     PatternInfo&  timeItvPtnInfo = fIntervalPatterns[itvPtnIndex];
1651     if ( !timeItvPtnInfo.firstPart.isEmpty() ) {
1652         UnicodeString timeIntervalPattern(timeItvPtnInfo.firstPart);
1653         timeIntervalPattern.append(timeItvPtnInfo.secondPart);
1654         UnicodeString combinedPattern;
1655         SimpleFormatter(format, 2, 2, status).
1656                 format(timeIntervalPattern, datePattern, combinedPattern, status);
1657         if ( U_FAILURE(status) ) {
1658             return;
1659         }
1660         setIntervalPattern(field, combinedPattern, timeItvPtnInfo.laterDateFirst);
1661     }
1662     // else: fall back
1663     // it should not happen if the interval format defined is valid
1664 }
1665 
1666 
1667 
1668 const UChar
1669 DateIntervalFormat::fgCalendarFieldToPatternLetter[] =
1670 {
1671     /*GyM*/ CAP_G, LOW_Y, CAP_M,
1672     /*wWd*/ LOW_W, CAP_W, LOW_D,
1673     /*DEF*/ CAP_D, CAP_E, CAP_F,
1674     /*ahH*/ LOW_A, LOW_H, CAP_H,
1675     /*msS*/ LOW_M, LOW_S, CAP_S, // MINUTE, SECOND, MILLISECOND
1676     /*z.Y*/ LOW_Z, SPACE, CAP_Y, // ZONE_OFFSET, DST_OFFSET, YEAR_WOY,
1677     /*eug*/ LOW_E, LOW_U, LOW_G, // DOW_LOCAL, EXTENDED_YEAR, JULIAN_DAY,
1678     /*A..*/ CAP_A, SPACE, SPACE, // MILLISECONDS_IN_DAY, IS_LEAP_MONTH, FIELD_COUNT
1679 };
1680 
1681 
1682 
1683 U_NAMESPACE_END
1684 
1685 #endif
1686