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