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