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