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