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