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