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