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