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