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