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
34U_NAMESPACE_BEGIN
35
36
37
38#ifdef DTITVFMT_DEBUG
39#define PRINTMESG(msg) { std::cout << "(" << __FILE__ << ":" << __LINE__ << ") " << msg << "\n"; }
40#endif
41
42
43static 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
54static const char gDateTimePatternsTag[]="DateTimePatterns";
55
56
57// latestFirst:
58static 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:
61static 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
64UOBJECT_DEFINE_RTTI_IMPLEMENTATION(DateIntervalFormat)
65
66
67
68DateIntervalFormat* U_EXPORT2
69DateIntervalFormat::createInstance(const UnicodeString& skeleton,
70                                   UErrorCode& status) {
71    return createInstance(skeleton, Locale::getDefault(), status);
72}
73
74
75DateIntervalFormat* U_EXPORT2
76DateIntervalFormat::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
97DateIntervalFormat* U_EXPORT2
98DateIntervalFormat::createInstance(const UnicodeString& skeleton,
99                                   const DateIntervalInfo& dtitvinf,
100                                   UErrorCode& status) {
101    return createInstance(skeleton, Locale::getDefault(), dtitvinf, status);
102}
103
104
105DateIntervalFormat* U_EXPORT2
106DateIntervalFormat::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
115DateIntervalFormat::DateIntervalFormat()
116:   fInfo(NULL),
117    fDateFormat(NULL),
118    fFromCalendar(NULL),
119    fToCalendar(NULL),
120    fDtpng(NULL)
121{}
122
123
124DateIntervalFormat::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
135DateIntervalFormat&
136DateIntervalFormat::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
176DateIntervalFormat::~DateIntervalFormat() {
177    delete fInfo;
178    delete fDateFormat;
179    delete fFromCalendar;
180    delete fToCalendar;
181    delete fDtpng;
182}
183
184
185Format*
186DateIntervalFormat::clone(void) const {
187    return new DateIntervalFormat(*this);
188}
189
190
191UBool
192DateIntervalFormat::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
235UnicodeString&
236DateIntervalFormat::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
256UnicodeString&
257DateIntervalFormat::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
277UnicodeString&
278DateIntervalFormat::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
383void
384DateIntervalFormat::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
395const DateIntervalInfo*
396DateIntervalFormat::getDateIntervalInfo() const {
397    return fInfo;
398}
399
400
401void
402DateIntervalFormat::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
413const DateFormat*
414DateIntervalFormat::getDateFormat() const {
415    return fDateFormat;
416}
417
418
419void
420DateIntervalFormat::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
437void
438DateIntervalFormat::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
453const TimeZone&
454DateIntervalFormat::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
463DateIntervalFormat::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
510SimpleDateFormat* U_EXPORT2
511DateIntervalFormat::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
533DateIntervalFormat* U_EXPORT2
534DateIntervalFormat::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 */
587void
588DateIntervalFormat::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
771void  U_EXPORT2
772DateIntervalFormat::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 */
939UBool
940DateIntervalFormat::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
1017void
1018DateIntervalFormat::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
1034void
1035DateIntervalFormat::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
1060void
1061DateIntervalFormat::setIntervalPattern(UCalendarDateFields field,
1062                                       const UnicodeString& intervalPattern) {
1063    UBool order = fInfo->getDefaultOrder();
1064    setIntervalPattern(field, intervalPattern, order);
1065}
1066
1067
1068void
1069DateIntervalFormat::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 */
1131UBool
1132DateIntervalFormat::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
1205int32_t  U_EXPORT2
1206DateIntervalFormat::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
1280UnicodeString&
1281DateIntervalFormat::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
1312UBool  U_EXPORT2
1313DateIntervalFormat::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
1322void  U_EXPORT2
1323DateIntervalFormat::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
1433void
1434DateIntervalFormat::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
1468const UChar
1469DateIntervalFormat::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
1482U_NAMESPACE_END
1483
1484#endif
1485