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