1.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 1.2 +++ b/intl/icu/source/i18n/dtitvfmt.cpp Wed Dec 31 06:09:35 2014 +0100 1.3 @@ -0,0 +1,1479 @@ 1.4 +/******************************************************************************* 1.5 +* Copyright (C) 2008-2013, International Business Machines Corporation and 1.6 +* others. All Rights Reserved. 1.7 +******************************************************************************* 1.8 +* 1.9 +* File DTITVFMT.CPP 1.10 +* 1.11 +******************************************************************************* 1.12 +*/ 1.13 + 1.14 +#include "utypeinfo.h" // for 'typeid' to work 1.15 + 1.16 +#include "unicode/dtitvfmt.h" 1.17 + 1.18 +#if !UCONFIG_NO_FORMATTING 1.19 + 1.20 +//TODO: put in compilation 1.21 +//#define DTITVFMT_DEBUG 1 1.22 + 1.23 +#include "cstring.h" 1.24 +#include "unicode/msgfmt.h" 1.25 +#include "unicode/dtptngen.h" 1.26 +#include "unicode/dtitvinf.h" 1.27 +#include "unicode/calendar.h" 1.28 +#include "dtitv_impl.h" 1.29 + 1.30 +#ifdef DTITVFMT_DEBUG 1.31 +#include <iostream> 1.32 +#include "cstring.h" 1.33 +#endif 1.34 + 1.35 +#include "gregoimp.h" 1.36 + 1.37 +U_NAMESPACE_BEGIN 1.38 + 1.39 + 1.40 + 1.41 +#ifdef DTITVFMT_DEBUG 1.42 +#define PRINTMESG(msg) { std::cout << "(" << __FILE__ << ":" << __LINE__ << ") " << msg << "\n"; } 1.43 +#endif 1.44 + 1.45 + 1.46 +static const UChar gDateFormatSkeleton[][11] = { 1.47 +//yMMMMEEEEd 1.48 +{LOW_Y, CAP_M, CAP_M, CAP_M, CAP_M, CAP_E, CAP_E, CAP_E, CAP_E, LOW_D, 0}, 1.49 +//yMMMMd 1.50 +{LOW_Y, CAP_M, CAP_M, CAP_M, CAP_M, LOW_D, 0}, 1.51 +//yMMMd 1.52 +{LOW_Y, CAP_M, CAP_M, CAP_M, LOW_D, 0}, 1.53 +//yMd 1.54 +{LOW_Y, CAP_M, LOW_D, 0} }; 1.55 + 1.56 + 1.57 +static const char gDateTimePatternsTag[]="DateTimePatterns"; 1.58 + 1.59 + 1.60 +// latestFirst: 1.61 +static const UChar gLaterFirstPrefix[] = {LOW_L, LOW_A, LOW_T, LOW_E, LOW_S,LOW_T, CAP_F, LOW_I, LOW_R, LOW_S, LOW_T, COLON}; 1.62 + 1.63 +// earliestFirst: 1.64 +static const UChar gEarlierFirstPrefix[] = {LOW_E, LOW_A, LOW_R, LOW_L, LOW_I, LOW_E, LOW_S, LOW_T, CAP_F, LOW_I, LOW_R, LOW_S, LOW_T, COLON}; 1.65 + 1.66 + 1.67 +UOBJECT_DEFINE_RTTI_IMPLEMENTATION(DateIntervalFormat) 1.68 + 1.69 + 1.70 + 1.71 +DateIntervalFormat* U_EXPORT2 1.72 +DateIntervalFormat::createInstance(const UnicodeString& skeleton, 1.73 + UErrorCode& status) { 1.74 + return createInstance(skeleton, Locale::getDefault(), status); 1.75 +} 1.76 + 1.77 + 1.78 +DateIntervalFormat* U_EXPORT2 1.79 +DateIntervalFormat::createInstance(const UnicodeString& skeleton, 1.80 + const Locale& locale, 1.81 + UErrorCode& status) { 1.82 +#ifdef DTITVFMT_DEBUG 1.83 + char result[1000]; 1.84 + char result_1[1000]; 1.85 + char mesg[2000]; 1.86 + skeleton.extract(0, skeleton.length(), result, "UTF-8"); 1.87 + UnicodeString pat; 1.88 + ((SimpleDateFormat*)dtfmt)->toPattern(pat); 1.89 + pat.extract(0, pat.length(), result_1, "UTF-8"); 1.90 + sprintf(mesg, "skeleton: %s; pattern: %s\n", result, result_1); 1.91 + PRINTMESG(mesg) 1.92 +#endif 1.93 + 1.94 + DateIntervalInfo* dtitvinf = new DateIntervalInfo(locale, status); 1.95 + return create(locale, dtitvinf, &skeleton, status); 1.96 +} 1.97 + 1.98 + 1.99 + 1.100 +DateIntervalFormat* U_EXPORT2 1.101 +DateIntervalFormat::createInstance(const UnicodeString& skeleton, 1.102 + const DateIntervalInfo& dtitvinf, 1.103 + UErrorCode& status) { 1.104 + return createInstance(skeleton, Locale::getDefault(), dtitvinf, status); 1.105 +} 1.106 + 1.107 + 1.108 +DateIntervalFormat* U_EXPORT2 1.109 +DateIntervalFormat::createInstance(const UnicodeString& skeleton, 1.110 + const Locale& locale, 1.111 + const DateIntervalInfo& dtitvinf, 1.112 + UErrorCode& status) { 1.113 + DateIntervalInfo* ptn = dtitvinf.clone(); 1.114 + return create(locale, ptn, &skeleton, status); 1.115 +} 1.116 + 1.117 + 1.118 +DateIntervalFormat::DateIntervalFormat() 1.119 +: fInfo(NULL), 1.120 + fDateFormat(NULL), 1.121 + fFromCalendar(NULL), 1.122 + fToCalendar(NULL), 1.123 + fDtpng(NULL) 1.124 +{} 1.125 + 1.126 + 1.127 +DateIntervalFormat::DateIntervalFormat(const DateIntervalFormat& itvfmt) 1.128 +: Format(itvfmt), 1.129 + fInfo(NULL), 1.130 + fDateFormat(NULL), 1.131 + fFromCalendar(NULL), 1.132 + fToCalendar(NULL), 1.133 + fDtpng(NULL) { 1.134 + *this = itvfmt; 1.135 +} 1.136 + 1.137 + 1.138 +DateIntervalFormat& 1.139 +DateIntervalFormat::operator=(const DateIntervalFormat& itvfmt) { 1.140 + if ( this != &itvfmt ) { 1.141 + delete fDateFormat; 1.142 + delete fInfo; 1.143 + delete fFromCalendar; 1.144 + delete fToCalendar; 1.145 + delete fDtpng; 1.146 + if ( itvfmt.fDateFormat ) { 1.147 + fDateFormat = (SimpleDateFormat*)itvfmt.fDateFormat->clone(); 1.148 + } else { 1.149 + fDateFormat = NULL; 1.150 + } 1.151 + if ( itvfmt.fInfo ) { 1.152 + fInfo = itvfmt.fInfo->clone(); 1.153 + } else { 1.154 + fInfo = NULL; 1.155 + } 1.156 + if ( itvfmt.fFromCalendar ) { 1.157 + fFromCalendar = itvfmt.fFromCalendar->clone(); 1.158 + } else { 1.159 + fFromCalendar = NULL; 1.160 + } 1.161 + if ( itvfmt.fToCalendar ) { 1.162 + fToCalendar = itvfmt.fToCalendar->clone(); 1.163 + } else { 1.164 + fToCalendar = NULL; 1.165 + } 1.166 + fSkeleton = itvfmt.fSkeleton; 1.167 + int8_t i; 1.168 + for ( i = 0; i< DateIntervalInfo::kIPI_MAX_INDEX; ++i ) { 1.169 + fIntervalPatterns[i] = itvfmt.fIntervalPatterns[i]; 1.170 + } 1.171 + if (itvfmt.fDtpng) { 1.172 + fDtpng = itvfmt.fDtpng->clone(); 1.173 + } 1.174 + } 1.175 + return *this; 1.176 +} 1.177 + 1.178 + 1.179 +DateIntervalFormat::~DateIntervalFormat() { 1.180 + delete fInfo; 1.181 + delete fDateFormat; 1.182 + delete fFromCalendar; 1.183 + delete fToCalendar; 1.184 + delete fDtpng; 1.185 +} 1.186 + 1.187 + 1.188 +Format* 1.189 +DateIntervalFormat::clone(void) const { 1.190 + return new DateIntervalFormat(*this); 1.191 +} 1.192 + 1.193 + 1.194 +UBool 1.195 +DateIntervalFormat::operator==(const Format& other) const { 1.196 + if (typeid(*this) == typeid(other)) { 1.197 + const DateIntervalFormat* fmt = (DateIntervalFormat*)&other; 1.198 +#ifdef DTITVFMT_DEBUG 1.199 + UBool equal; 1.200 + equal = (this == fmt); 1.201 + 1.202 + equal = (*fInfo == *fmt->fInfo); 1.203 + equal = (*fDateFormat == *fmt->fDateFormat); 1.204 + equal = fFromCalendar->isEquivalentTo(*fmt->fFromCalendar) ; 1.205 + equal = fToCalendar->isEquivalentTo(*fmt->fToCalendar) ; 1.206 + equal = (fSkeleton == fmt->fSkeleton); 1.207 +#endif 1.208 + UBool res; 1.209 + res = ( this == fmt ) || 1.210 + ( Format::operator==(other) && 1.211 + fInfo && 1.212 + ( *fInfo == *fmt->fInfo ) && 1.213 + fDateFormat && 1.214 + ( *fDateFormat == *fmt->fDateFormat ) && 1.215 + fFromCalendar && 1.216 + fFromCalendar->isEquivalentTo(*fmt->fFromCalendar) && 1.217 + fToCalendar && 1.218 + fToCalendar->isEquivalentTo(*fmt->fToCalendar) && 1.219 + fSkeleton == fmt->fSkeleton && 1.220 + fDtpng && 1.221 + (*fDtpng == *fmt->fDtpng) ); 1.222 + int8_t i; 1.223 + for (i = 0; i< DateIntervalInfo::kIPI_MAX_INDEX && res == TRUE; ++i ) { 1.224 + res = ( fIntervalPatterns[i].firstPart == 1.225 + fmt->fIntervalPatterns[i].firstPart) && 1.226 + ( fIntervalPatterns[i].secondPart == 1.227 + fmt->fIntervalPatterns[i].secondPart ) && 1.228 + ( fIntervalPatterns[i].laterDateFirst == 1.229 + fmt->fIntervalPatterns[i].laterDateFirst) ; 1.230 + } 1.231 + return res; 1.232 + } 1.233 + return FALSE; 1.234 +} 1.235 + 1.236 + 1.237 + 1.238 +UnicodeString& 1.239 +DateIntervalFormat::format(const Formattable& obj, 1.240 + UnicodeString& appendTo, 1.241 + FieldPosition& fieldPosition, 1.242 + UErrorCode& status) const { 1.243 + if ( U_FAILURE(status) ) { 1.244 + return appendTo; 1.245 + } 1.246 + 1.247 + if ( obj.getType() == Formattable::kObject ) { 1.248 + const UObject* formatObj = obj.getObject(); 1.249 + const DateInterval* interval = dynamic_cast<const DateInterval*>(formatObj); 1.250 + if (interval != NULL){ 1.251 + return format(interval, appendTo, fieldPosition, status); 1.252 + } 1.253 + } 1.254 + status = U_ILLEGAL_ARGUMENT_ERROR; 1.255 + return appendTo; 1.256 +} 1.257 + 1.258 + 1.259 +UnicodeString& 1.260 +DateIntervalFormat::format(const DateInterval* dtInterval, 1.261 + UnicodeString& appendTo, 1.262 + FieldPosition& fieldPosition, 1.263 + UErrorCode& status) const { 1.264 + if ( U_FAILURE(status) ) { 1.265 + return appendTo; 1.266 + } 1.267 + 1.268 + if ( fFromCalendar != NULL && fToCalendar != NULL && 1.269 + fDateFormat != NULL && fInfo != NULL ) { 1.270 + fFromCalendar->setTime(dtInterval->getFromDate(), status); 1.271 + fToCalendar->setTime(dtInterval->getToDate(), status); 1.272 + if ( U_SUCCESS(status) ) { 1.273 + return format(*fFromCalendar, *fToCalendar, appendTo,fieldPosition, status); 1.274 + } 1.275 + } 1.276 + return appendTo; 1.277 +} 1.278 + 1.279 + 1.280 +UnicodeString& 1.281 +DateIntervalFormat::format(Calendar& fromCalendar, 1.282 + Calendar& toCalendar, 1.283 + UnicodeString& appendTo, 1.284 + FieldPosition& pos, 1.285 + UErrorCode& status) const { 1.286 + if ( U_FAILURE(status) ) { 1.287 + return appendTo; 1.288 + } 1.289 + 1.290 + // not support different calendar types and time zones 1.291 + //if ( fromCalendar.getType() != toCalendar.getType() ) { 1.292 + if ( !fromCalendar.isEquivalentTo(toCalendar) ) { 1.293 + status = U_ILLEGAL_ARGUMENT_ERROR; 1.294 + return appendTo; 1.295 + } 1.296 + 1.297 + // First, find the largest different calendar field. 1.298 + UCalendarDateFields field = UCAL_FIELD_COUNT; 1.299 + 1.300 + if ( fromCalendar.get(UCAL_ERA,status) != toCalendar.get(UCAL_ERA,status)) { 1.301 + field = UCAL_ERA; 1.302 + } else if ( fromCalendar.get(UCAL_YEAR, status) != 1.303 + toCalendar.get(UCAL_YEAR, status) ) { 1.304 + field = UCAL_YEAR; 1.305 + } else if ( fromCalendar.get(UCAL_MONTH, status) != 1.306 + toCalendar.get(UCAL_MONTH, status) ) { 1.307 + field = UCAL_MONTH; 1.308 + } else if ( fromCalendar.get(UCAL_DATE, status) != 1.309 + toCalendar.get(UCAL_DATE, status) ) { 1.310 + field = UCAL_DATE; 1.311 + } else if ( fromCalendar.get(UCAL_AM_PM, status) != 1.312 + toCalendar.get(UCAL_AM_PM, status) ) { 1.313 + field = UCAL_AM_PM; 1.314 + } else if ( fromCalendar.get(UCAL_HOUR, status) != 1.315 + toCalendar.get(UCAL_HOUR, status) ) { 1.316 + field = UCAL_HOUR; 1.317 + } else if ( fromCalendar.get(UCAL_MINUTE, status) != 1.318 + toCalendar.get(UCAL_MINUTE, status) ) { 1.319 + field = UCAL_MINUTE; 1.320 + } 1.321 + 1.322 + if ( U_FAILURE(status) ) { 1.323 + return appendTo; 1.324 + } 1.325 + if ( field == UCAL_FIELD_COUNT ) { 1.326 + /* ignore the second/millisecond etc. small fields' difference. 1.327 + * use single date when all the above are the same. 1.328 + */ 1.329 + return fDateFormat->format(fromCalendar, appendTo, pos); 1.330 + } 1.331 + 1.332 + // following call should not set wrong status, 1.333 + // all the pass-in fields are valid till here 1.334 + int32_t itvPtnIndex = DateIntervalInfo::calendarFieldToIntervalIndex(field, 1.335 + status); 1.336 + const PatternInfo& intervalPattern = fIntervalPatterns[itvPtnIndex]; 1.337 + 1.338 + if ( intervalPattern.firstPart.isEmpty() && 1.339 + intervalPattern.secondPart.isEmpty() ) { 1.340 + if ( fDateFormat->isFieldUnitIgnored(field) ) { 1.341 + /* the largest different calendar field is small than 1.342 + * the smallest calendar field in pattern, 1.343 + * return single date format. 1.344 + */ 1.345 + return fDateFormat->format(fromCalendar, appendTo, pos); 1.346 + } 1.347 + return fallbackFormat(fromCalendar, toCalendar, appendTo, pos, status); 1.348 + } 1.349 + // If the first part in interval pattern is empty, 1.350 + // the 2nd part of it saves the full-pattern used in fall-back. 1.351 + // For a 'real' interval pattern, the first part will never be empty. 1.352 + if ( intervalPattern.firstPart.isEmpty() ) { 1.353 + // fall back 1.354 + UnicodeString originalPattern; 1.355 + fDateFormat->toPattern(originalPattern); 1.356 + fDateFormat->applyPattern(intervalPattern.secondPart); 1.357 + appendTo = fallbackFormat(fromCalendar, toCalendar, appendTo, pos, status); 1.358 + fDateFormat->applyPattern(originalPattern); 1.359 + return appendTo; 1.360 + } 1.361 + Calendar* firstCal; 1.362 + Calendar* secondCal; 1.363 + if ( intervalPattern.laterDateFirst ) { 1.364 + firstCal = &toCalendar; 1.365 + secondCal = &fromCalendar; 1.366 + } else { 1.367 + firstCal = &fromCalendar; 1.368 + secondCal = &toCalendar; 1.369 + } 1.370 + // break the interval pattern into 2 parts, 1.371 + // first part should not be empty, 1.372 + UnicodeString originalPattern; 1.373 + fDateFormat->toPattern(originalPattern); 1.374 + fDateFormat->applyPattern(intervalPattern.firstPart); 1.375 + fDateFormat->format(*firstCal, appendTo, pos); 1.376 + if ( !intervalPattern.secondPart.isEmpty() ) { 1.377 + fDateFormat->applyPattern(intervalPattern.secondPart); 1.378 + fDateFormat->format(*secondCal, appendTo, pos); 1.379 + } 1.380 + fDateFormat->applyPattern(originalPattern); 1.381 + return appendTo; 1.382 +} 1.383 + 1.384 + 1.385 + 1.386 +void 1.387 +DateIntervalFormat::parseObject(const UnicodeString& /* source */, 1.388 + Formattable& /* result */, 1.389 + ParsePosition& /* parse_pos */) const { 1.390 + // parseObject(const UnicodeString&, Formattable&, UErrorCode&) const 1.391 + // will set status as U_INVALID_FORMAT_ERROR if 1.392 + // parse_pos is still 0 1.393 +} 1.394 + 1.395 + 1.396 + 1.397 + 1.398 +const DateIntervalInfo* 1.399 +DateIntervalFormat::getDateIntervalInfo() const { 1.400 + return fInfo; 1.401 +} 1.402 + 1.403 + 1.404 +void 1.405 +DateIntervalFormat::setDateIntervalInfo(const DateIntervalInfo& newItvPattern, 1.406 + UErrorCode& status) { 1.407 + delete fInfo; 1.408 + fInfo = new DateIntervalInfo(newItvPattern); 1.409 + if ( fDateFormat ) { 1.410 + initializePattern(status); 1.411 + } 1.412 +} 1.413 + 1.414 + 1.415 + 1.416 +const DateFormat* 1.417 +DateIntervalFormat::getDateFormat() const { 1.418 + return fDateFormat; 1.419 +} 1.420 + 1.421 + 1.422 +void 1.423 +DateIntervalFormat::adoptTimeZone(TimeZone* zone) 1.424 +{ 1.425 + if (fDateFormat != NULL) { 1.426 + fDateFormat->adoptTimeZone(zone); 1.427 + } 1.428 + // The fDateFormat has the master calendar for the DateIntervalFormat and has 1.429 + // ownership of any adopted TimeZone; fFromCalendar and fToCalendar are internal 1.430 + // work clones of that calendar (and should not also be given ownership of the 1.431 + // adopted TimeZone). 1.432 + if (fFromCalendar) { 1.433 + fFromCalendar->setTimeZone(*zone); 1.434 + } 1.435 + if (fToCalendar) { 1.436 + fToCalendar->setTimeZone(*zone); 1.437 + } 1.438 +} 1.439 + 1.440 +void 1.441 +DateIntervalFormat::setTimeZone(const TimeZone& zone) 1.442 +{ 1.443 + if (fDateFormat != NULL) { 1.444 + fDateFormat->setTimeZone(zone); 1.445 + } 1.446 + // The fDateFormat has the master calendar for the DateIntervalFormat; 1.447 + // fFromCalendar and fToCalendar are internal work clones of that calendar. 1.448 + if (fFromCalendar) { 1.449 + fFromCalendar->setTimeZone(zone); 1.450 + } 1.451 + if (fToCalendar) { 1.452 + fToCalendar->setTimeZone(zone); 1.453 + } 1.454 +} 1.455 + 1.456 +const TimeZone& 1.457 +DateIntervalFormat::getTimeZone() const 1.458 +{ 1.459 + if (fDateFormat != NULL) { 1.460 + return fDateFormat->getTimeZone(); 1.461 + } 1.462 + // If fDateFormat is NULL (unexpected), create default timezone. 1.463 + return *(TimeZone::createDefault()); 1.464 +} 1.465 + 1.466 +DateIntervalFormat::DateIntervalFormat(const Locale& locale, 1.467 + DateIntervalInfo* dtItvInfo, 1.468 + const UnicodeString* skeleton, 1.469 + UErrorCode& status) 1.470 +: fInfo(NULL), 1.471 + fDateFormat(NULL), 1.472 + fFromCalendar(NULL), 1.473 + fToCalendar(NULL), 1.474 + fDtpng(NULL) 1.475 +{ 1.476 + if ( U_FAILURE(status) ) { 1.477 + delete dtItvInfo; 1.478 + return; 1.479 + } 1.480 + fDtpng = DateTimePatternGenerator::createInstance(locale, status); 1.481 + SimpleDateFormat* dtfmt = createSDFPatternInstance(*skeleton, locale, 1.482 + fDtpng, status); 1.483 + if ( U_FAILURE(status) ) { 1.484 + delete dtItvInfo; 1.485 + delete fDtpng; 1.486 + delete dtfmt; 1.487 + return; 1.488 + } 1.489 + if ( dtfmt == NULL || dtItvInfo == NULL || fDtpng == NULL ) { 1.490 + status = U_MEMORY_ALLOCATION_ERROR; 1.491 + // safe to delete NULL 1.492 + delete dtfmt; 1.493 + delete dtItvInfo; 1.494 + delete fDtpng; 1.495 + return; 1.496 + } 1.497 + if ( skeleton ) { 1.498 + fSkeleton = *skeleton; 1.499 + } 1.500 + fInfo = dtItvInfo; 1.501 + fDateFormat = dtfmt; 1.502 + if ( dtfmt->getCalendar() ) { 1.503 + fFromCalendar = dtfmt->getCalendar()->clone(); 1.504 + fToCalendar = dtfmt->getCalendar()->clone(); 1.505 + } else { 1.506 + fFromCalendar = NULL; 1.507 + fToCalendar = NULL; 1.508 + } 1.509 + initializePattern(status); 1.510 +} 1.511 + 1.512 + 1.513 +SimpleDateFormat* U_EXPORT2 1.514 +DateIntervalFormat::createSDFPatternInstance(const UnicodeString& skeleton, 1.515 + const Locale& locale, 1.516 + DateTimePatternGenerator* dtpng, 1.517 + UErrorCode& status) 1.518 +{ 1.519 + if ( U_FAILURE(status) ) { 1.520 + return NULL; 1.521 + } 1.522 + 1.523 + const UnicodeString pattern = dtpng->getBestPattern(skeleton, status); 1.524 + if ( U_FAILURE(status) ) { 1.525 + return NULL; 1.526 + } 1.527 + SimpleDateFormat* dtfmt = new SimpleDateFormat(pattern, locale, status); 1.528 + if ( U_FAILURE(status) ) { 1.529 + delete dtfmt; 1.530 + return NULL; 1.531 + } 1.532 + return dtfmt; 1.533 +} 1.534 + 1.535 + 1.536 +DateIntervalFormat* U_EXPORT2 1.537 +DateIntervalFormat::create(const Locale& locale, 1.538 + DateIntervalInfo* dtitvinf, 1.539 + const UnicodeString* skeleton, 1.540 + UErrorCode& status) { 1.541 + DateIntervalFormat* f = new DateIntervalFormat(locale, dtitvinf, 1.542 + skeleton, status); 1.543 + if ( f == NULL ) { 1.544 + status = U_MEMORY_ALLOCATION_ERROR; 1.545 + delete dtitvinf; 1.546 + } else if ( U_FAILURE(status) ) { 1.547 + // safe to delete f, although nothing acutally is saved 1.548 + delete f; 1.549 + f = 0; 1.550 + } 1.551 + return f; 1.552 +} 1.553 + 1.554 + 1.555 + 1.556 +/** 1.557 + * Initialize interval patterns locale to this formatter 1.558 + * 1.559 + * This code is a bit complicated since 1.560 + * 1. the interval patterns saved in resource bundle files are interval 1.561 + * patterns based on date or time only. 1.562 + * It does not have interval patterns based on both date and time. 1.563 + * Interval patterns on both date and time are algorithm generated. 1.564 + * 1.565 + * For example, it has interval patterns on skeleton "dMy" and "hm", 1.566 + * but it does not have interval patterns on skeleton "dMyhm". 1.567 + * 1.568 + * The rule to genearte interval patterns for both date and time skeleton are 1.569 + * 1) when the year, month, or day differs, concatenate the two original 1.570 + * expressions with a separator between, 1.571 + * For example, interval pattern from "Jan 10, 2007 10:10 am" 1.572 + * to "Jan 11, 2007 10:10am" is 1.573 + * "Jan 10, 2007 10:10 am - Jan 11, 2007 10:10am" 1.574 + * 1.575 + * 2) otherwise, present the date followed by the range expression 1.576 + * for the time. 1.577 + * For example, interval pattern from "Jan 10, 2007 10:10 am" 1.578 + * to "Jan 10, 2007 11:10am" is 1.579 + * "Jan 10, 2007 10:10 am - 11:10am" 1.580 + * 1.581 + * 2. even a pattern does not request a certion calendar field, 1.582 + * the interval pattern needs to include such field if such fields are 1.583 + * different between 2 dates. 1.584 + * For example, a pattern/skeleton is "hm", but the interval pattern 1.585 + * includes year, month, and date when year, month, and date differs. 1.586 + * 1.587 + * @param status output param set to success/failure code on exit 1.588 + * @stable ICU 4.0 1.589 + */ 1.590 +void 1.591 +DateIntervalFormat::initializePattern(UErrorCode& status) { 1.592 + if ( U_FAILURE(status) ) { 1.593 + return; 1.594 + } 1.595 + const Locale& locale = fDateFormat->getSmpFmtLocale(); 1.596 + if ( fSkeleton.isEmpty() ) { 1.597 + UnicodeString fullPattern; 1.598 + fDateFormat->toPattern(fullPattern); 1.599 +#ifdef DTITVFMT_DEBUG 1.600 + char result[1000]; 1.601 + char result_1[1000]; 1.602 + char mesg[2000]; 1.603 + fSkeleton.extract(0, fSkeleton.length(), result, "UTF-8"); 1.604 + sprintf(mesg, "in getBestSkeleton: fSkeleton: %s; \n", result); 1.605 + PRINTMESG(mesg) 1.606 +#endif 1.607 + // fSkeleton is already set by createDateIntervalInstance() 1.608 + // or by createInstance(UnicodeString skeleton, .... ) 1.609 + fSkeleton = fDtpng->getSkeleton(fullPattern, status); 1.610 + if ( U_FAILURE(status) ) { 1.611 + return; 1.612 + } 1.613 + } 1.614 + 1.615 + // initialize the fIntervalPattern ordering 1.616 + int8_t i; 1.617 + for ( i = 0; i < DateIntervalInfo::kIPI_MAX_INDEX; ++i ) { 1.618 + fIntervalPatterns[i].laterDateFirst = fInfo->getDefaultOrder(); 1.619 + } 1.620 + 1.621 + /* Check whether the skeleton is a combination of date and time. 1.622 + * For the complication reason 1 explained above. 1.623 + */ 1.624 + UnicodeString dateSkeleton; 1.625 + UnicodeString timeSkeleton; 1.626 + UnicodeString normalizedTimeSkeleton; 1.627 + UnicodeString normalizedDateSkeleton; 1.628 + 1.629 + 1.630 + /* the difference between time skeleton and normalizedTimeSkeleton are: 1.631 + * 1. (Formerly, normalized time skeleton folded 'H' to 'h'; no longer true) 1.632 + * 2. 'a' is omitted in normalized time skeleton. 1.633 + * 3. there is only one appearance for 'h' or 'H', 'm','v', 'z' in normalized 1.634 + * time skeleton 1.635 + * 1.636 + * The difference between date skeleton and normalizedDateSkeleton are: 1.637 + * 1. both 'y' and 'd' appear only once in normalizeDateSkeleton 1.638 + * 2. 'E' and 'EE' are normalized into 'EEE' 1.639 + * 3. 'MM' is normalized into 'M' 1.640 + */ 1.641 + getDateTimeSkeleton(fSkeleton, dateSkeleton, normalizedDateSkeleton, 1.642 + timeSkeleton, normalizedTimeSkeleton); 1.643 + 1.644 +#ifdef DTITVFMT_DEBUG 1.645 + char result[1000]; 1.646 + char result_1[1000]; 1.647 + char mesg[2000]; 1.648 + fSkeleton.extract(0, fSkeleton.length(), result, "UTF-8"); 1.649 + sprintf(mesg, "in getBestSkeleton: fSkeleton: %s; \n", result); 1.650 + PRINTMESG(mesg) 1.651 +#endif 1.652 + 1.653 + 1.654 + UBool found = setSeparateDateTimePtn(normalizedDateSkeleton, 1.655 + normalizedTimeSkeleton); 1.656 + 1.657 + if ( found == false ) { 1.658 + // use fallback 1.659 + // TODO: if user asks "m"(minute), but "d"(day) differ 1.660 + if ( timeSkeleton.length() != 0 ) { 1.661 + if ( dateSkeleton.length() == 0 ) { 1.662 + // prefix with yMd 1.663 + timeSkeleton.insert(0, gDateFormatSkeleton[DateFormat::kShort], -1); 1.664 + UnicodeString pattern = fDtpng->getBestPattern(timeSkeleton, status); 1.665 + if ( U_FAILURE(status) ) { 1.666 + return; 1.667 + } 1.668 + // for fall back interval patterns, 1.669 + // the first part of the pattern is empty, 1.670 + // the second part of the pattern is the full-pattern 1.671 + // should be used in fall-back. 1.672 + setPatternInfo(UCAL_DATE, NULL, &pattern, fInfo->getDefaultOrder()); 1.673 + setPatternInfo(UCAL_MONTH, NULL, &pattern, fInfo->getDefaultOrder()); 1.674 + setPatternInfo(UCAL_YEAR, NULL, &pattern, fInfo->getDefaultOrder()); 1.675 + } else { 1.676 + // TODO: fall back 1.677 + } 1.678 + } else { 1.679 + // TODO: fall back 1.680 + } 1.681 + return; 1.682 + } // end of skeleton not found 1.683 + // interval patterns for skeleton are found in resource 1.684 + if ( timeSkeleton.length() == 0 ) { 1.685 + // done 1.686 + } else if ( dateSkeleton.length() == 0 ) { 1.687 + // prefix with yMd 1.688 + timeSkeleton.insert(0, gDateFormatSkeleton[DateFormat::kShort], -1); 1.689 + UnicodeString pattern = fDtpng->getBestPattern(timeSkeleton, status); 1.690 + if ( U_FAILURE(status) ) { 1.691 + return; 1.692 + } 1.693 + // for fall back interval patterns, 1.694 + // the first part of the pattern is empty, 1.695 + // the second part of the pattern is the full-pattern 1.696 + // should be used in fall-back. 1.697 + setPatternInfo(UCAL_DATE, NULL, &pattern, fInfo->getDefaultOrder()); 1.698 + setPatternInfo(UCAL_MONTH, NULL, &pattern, fInfo->getDefaultOrder()); 1.699 + setPatternInfo(UCAL_YEAR, NULL, &pattern, fInfo->getDefaultOrder()); 1.700 + } else { 1.701 + /* if both present, 1.702 + * 1) when the year, month, or day differs, 1.703 + * concatenate the two original expressions with a separator between, 1.704 + * 2) otherwise, present the date followed by the 1.705 + * range expression for the time. 1.706 + */ 1.707 + /* 1.708 + * 1) when the year, month, or day differs, 1.709 + * concatenate the two original expressions with a separator between, 1.710 + */ 1.711 + // if field exists, use fall back 1.712 + UnicodeString skeleton = fSkeleton; 1.713 + if ( !fieldExistsInSkeleton(UCAL_DATE, dateSkeleton) ) { 1.714 + // prefix skeleton with 'd' 1.715 + skeleton.insert(0, LOW_D); 1.716 + setFallbackPattern(UCAL_DATE, skeleton, status); 1.717 + } 1.718 + if ( !fieldExistsInSkeleton(UCAL_MONTH, dateSkeleton) ) { 1.719 + // then prefix skeleton with 'M' 1.720 + skeleton.insert(0, CAP_M); 1.721 + setFallbackPattern(UCAL_MONTH, skeleton, status); 1.722 + } 1.723 + if ( !fieldExistsInSkeleton(UCAL_YEAR, dateSkeleton) ) { 1.724 + // then prefix skeleton with 'y' 1.725 + skeleton.insert(0, LOW_Y); 1.726 + setFallbackPattern(UCAL_YEAR, skeleton, status); 1.727 + } 1.728 + 1.729 + /* 1.730 + * 2) otherwise, present the date followed by the 1.731 + * range expression for the time. 1.732 + */ 1.733 + // Need the Date/Time pattern for concatnation the date with 1.734 + // the time interval. 1.735 + // The date/time pattern ( such as {0} {1} ) is saved in 1.736 + // calendar, that is why need to get the CalendarData here. 1.737 + CalendarData* calData = new CalendarData(locale, NULL, status); 1.738 + 1.739 + if ( U_FAILURE(status) ) { 1.740 + delete calData; 1.741 + return; 1.742 + } 1.743 + 1.744 + if ( calData == NULL ) { 1.745 + status = U_MEMORY_ALLOCATION_ERROR; 1.746 + return; 1.747 + } 1.748 + 1.749 + const UResourceBundle* dateTimePatternsRes = calData->getByKey( 1.750 + gDateTimePatternsTag, status); 1.751 + int32_t dateTimeFormatLength; 1.752 + const UChar* dateTimeFormat = ures_getStringByIndex( 1.753 + dateTimePatternsRes, 1.754 + (int32_t)DateFormat::kDateTime, 1.755 + &dateTimeFormatLength, &status); 1.756 + if ( U_FAILURE(status) ) { 1.757 + return; 1.758 + } 1.759 + 1.760 + UnicodeString datePattern = fDtpng->getBestPattern(dateSkeleton, status); 1.761 + 1.762 + concatSingleDate2TimeInterval(dateTimeFormat, dateTimeFormatLength, 1.763 + datePattern, UCAL_AM_PM, status); 1.764 + concatSingleDate2TimeInterval(dateTimeFormat, dateTimeFormatLength, 1.765 + datePattern, UCAL_HOUR, status); 1.766 + concatSingleDate2TimeInterval(dateTimeFormat, dateTimeFormatLength, 1.767 + datePattern, UCAL_MINUTE, status); 1.768 + delete calData; 1.769 + } 1.770 +} 1.771 + 1.772 + 1.773 + 1.774 +void U_EXPORT2 1.775 +DateIntervalFormat::getDateTimeSkeleton(const UnicodeString& skeleton, 1.776 + UnicodeString& dateSkeleton, 1.777 + UnicodeString& normalizedDateSkeleton, 1.778 + UnicodeString& timeSkeleton, 1.779 + UnicodeString& normalizedTimeSkeleton) { 1.780 + // dateSkeleton follows the sequence of y*M*E*d* 1.781 + // timeSkeleton follows the sequence of hm*[v|z]? 1.782 + int32_t ECount = 0; 1.783 + int32_t dCount = 0; 1.784 + int32_t MCount = 0; 1.785 + int32_t yCount = 0; 1.786 + int32_t hCount = 0; 1.787 + int32_t HCount = 0; 1.788 + int32_t mCount = 0; 1.789 + int32_t vCount = 0; 1.790 + int32_t zCount = 0; 1.791 + int32_t i; 1.792 + 1.793 + for (i = 0; i < skeleton.length(); ++i) { 1.794 + UChar ch = skeleton[i]; 1.795 + switch ( ch ) { 1.796 + case CAP_E: 1.797 + dateSkeleton.append(ch); 1.798 + ++ECount; 1.799 + break; 1.800 + case LOW_D: 1.801 + dateSkeleton.append(ch); 1.802 + ++dCount; 1.803 + break; 1.804 + case CAP_M: 1.805 + dateSkeleton.append(ch); 1.806 + ++MCount; 1.807 + break; 1.808 + case LOW_Y: 1.809 + dateSkeleton.append(ch); 1.810 + ++yCount; 1.811 + break; 1.812 + case CAP_G: 1.813 + case CAP_Y: 1.814 + case LOW_U: 1.815 + case CAP_Q: 1.816 + case LOW_Q: 1.817 + case CAP_L: 1.818 + case LOW_L: 1.819 + case CAP_W: 1.820 + case LOW_W: 1.821 + case CAP_D: 1.822 + case CAP_F: 1.823 + case LOW_G: 1.824 + case LOW_E: 1.825 + case LOW_C: 1.826 + normalizedDateSkeleton.append(ch); 1.827 + dateSkeleton.append(ch); 1.828 + break; 1.829 + case LOW_A: 1.830 + // 'a' is implicitly handled 1.831 + timeSkeleton.append(ch); 1.832 + break; 1.833 + case LOW_H: 1.834 + timeSkeleton.append(ch); 1.835 + ++hCount; 1.836 + break; 1.837 + case CAP_H: 1.838 + timeSkeleton.append(ch); 1.839 + ++HCount; 1.840 + break; 1.841 + case LOW_M: 1.842 + timeSkeleton.append(ch); 1.843 + ++mCount; 1.844 + break; 1.845 + case LOW_Z: 1.846 + ++zCount; 1.847 + timeSkeleton.append(ch); 1.848 + break; 1.849 + case LOW_V: 1.850 + ++vCount; 1.851 + timeSkeleton.append(ch); 1.852 + break; 1.853 + case CAP_V: 1.854 + case CAP_Z: 1.855 + case LOW_K: 1.856 + case CAP_K: 1.857 + case LOW_J: 1.858 + case LOW_S: 1.859 + case CAP_S: 1.860 + case CAP_A: 1.861 + timeSkeleton.append(ch); 1.862 + normalizedTimeSkeleton.append(ch); 1.863 + break; 1.864 + } 1.865 + } 1.866 + 1.867 + /* generate normalized form for date*/ 1.868 + if ( yCount != 0 ) { 1.869 + for (i = 0; i < yCount; ++i) { 1.870 + normalizedDateSkeleton.append(LOW_Y); 1.871 + } 1.872 + } 1.873 + if ( MCount != 0 ) { 1.874 + if ( MCount < 3 ) { 1.875 + normalizedDateSkeleton.append(CAP_M); 1.876 + } else { 1.877 + int32_t i; 1.878 + for ( i = 0; i < MCount && i < MAX_M_COUNT; ++i ) { 1.879 + normalizedDateSkeleton.append(CAP_M); 1.880 + } 1.881 + } 1.882 + } 1.883 + if ( ECount != 0 ) { 1.884 + if ( ECount <= 3 ) { 1.885 + normalizedDateSkeleton.append(CAP_E); 1.886 + } else { 1.887 + int32_t i; 1.888 + for ( i = 0; i < ECount && i < MAX_E_COUNT; ++i ) { 1.889 + normalizedDateSkeleton.append(CAP_E); 1.890 + } 1.891 + } 1.892 + } 1.893 + if ( dCount != 0 ) { 1.894 + normalizedDateSkeleton.append(LOW_D); 1.895 + } 1.896 + 1.897 + /* generate normalized form for time */ 1.898 + if ( HCount != 0 ) { 1.899 + normalizedTimeSkeleton.append(CAP_H); 1.900 + } 1.901 + else if ( hCount != 0 ) { 1.902 + normalizedTimeSkeleton.append(LOW_H); 1.903 + } 1.904 + if ( mCount != 0 ) { 1.905 + normalizedTimeSkeleton.append(LOW_M); 1.906 + } 1.907 + if ( zCount != 0 ) { 1.908 + normalizedTimeSkeleton.append(LOW_Z); 1.909 + } 1.910 + if ( vCount != 0 ) { 1.911 + normalizedTimeSkeleton.append(LOW_V); 1.912 + } 1.913 +} 1.914 + 1.915 + 1.916 +/** 1.917 + * Generate date or time interval pattern from resource, 1.918 + * and set them into the interval pattern locale to this formatter. 1.919 + * 1.920 + * It needs to handle the following: 1.921 + * 1. need to adjust field width. 1.922 + * For example, the interval patterns saved in DateIntervalInfo 1.923 + * includes "dMMMy", but not "dMMMMy". 1.924 + * Need to get interval patterns for dMMMMy from dMMMy. 1.925 + * Another example, the interval patterns saved in DateIntervalInfo 1.926 + * includes "hmv", but not "hmz". 1.927 + * Need to get interval patterns for "hmz' from 'hmv' 1.928 + * 1.929 + * 2. there might be no pattern for 'y' differ for skeleton "Md", 1.930 + * in order to get interval patterns for 'y' differ, 1.931 + * need to look for it from skeleton 'yMd' 1.932 + * 1.933 + * @param dateSkeleton normalized date skeleton 1.934 + * @param timeSkeleton normalized time skeleton 1.935 + * @return whether the resource is found for the skeleton. 1.936 + * TRUE if interval pattern found for the skeleton, 1.937 + * FALSE otherwise. 1.938 + * @stable ICU 4.0 1.939 + */ 1.940 +UBool 1.941 +DateIntervalFormat::setSeparateDateTimePtn( 1.942 + const UnicodeString& dateSkeleton, 1.943 + const UnicodeString& timeSkeleton) { 1.944 + const UnicodeString* skeleton; 1.945 + // if both date and time skeleton present, 1.946 + // the final interval pattern might include time interval patterns 1.947 + // ( when, am_pm, hour, minute differ ), 1.948 + // but not date interval patterns ( when year, month, day differ ). 1.949 + // For year/month/day differ, it falls back to fall-back pattern. 1.950 + if ( timeSkeleton.length() != 0 ) { 1.951 + skeleton = &timeSkeleton; 1.952 + } else { 1.953 + skeleton = &dateSkeleton; 1.954 + } 1.955 + 1.956 + /* interval patterns for skeleton "dMMMy" (but not "dMMMMy") 1.957 + * are defined in resource, 1.958 + * interval patterns for skeleton "dMMMMy" are calculated by 1.959 + * 1. get the best match skeleton for "dMMMMy", which is "dMMMy" 1.960 + * 2. get the interval patterns for "dMMMy", 1.961 + * 3. extend "MMM" to "MMMM" in above interval patterns for "dMMMMy" 1.962 + * getBestSkeleton() is step 1. 1.963 + */ 1.964 + // best skeleton, and the difference information 1.965 + int8_t differenceInfo = 0; 1.966 + const UnicodeString* bestSkeleton = fInfo->getBestSkeleton(*skeleton, 1.967 + differenceInfo); 1.968 + /* best skeleton could be NULL. 1.969 + For example: in "ca" resource file, 1.970 + interval format is defined as following 1.971 + intervalFormats{ 1.972 + fallback{"{0} - {1}"} 1.973 + } 1.974 + there is no skeletons/interval patterns defined, 1.975 + and the best skeleton match could be NULL 1.976 + */ 1.977 + if ( bestSkeleton == NULL ) { 1.978 + return false; 1.979 + } 1.980 + 1.981 + // difference: 1.982 + // 0 means the best matched skeleton is the same as input skeleton 1.983 + // 1 means the fields are the same, but field width are different 1.984 + // 2 means the only difference between fields are v/z, 1.985 + // -1 means there are other fields difference 1.986 + if ( differenceInfo == -1 ) { 1.987 + // skeleton has different fields, not only v/z difference 1.988 + return false; 1.989 + } 1.990 + 1.991 + if ( timeSkeleton.length() == 0 ) { 1.992 + UnicodeString extendedSkeleton; 1.993 + UnicodeString extendedBestSkeleton; 1.994 + // only has date skeleton 1.995 + setIntervalPattern(UCAL_DATE, skeleton, bestSkeleton, differenceInfo, 1.996 + &extendedSkeleton, &extendedBestSkeleton); 1.997 + 1.998 + UBool extended = setIntervalPattern(UCAL_MONTH, skeleton, bestSkeleton, 1.999 + differenceInfo, 1.1000 + &extendedSkeleton, &extendedBestSkeleton); 1.1001 + 1.1002 + if ( extended ) { 1.1003 + bestSkeleton = &extendedBestSkeleton; 1.1004 + skeleton = &extendedSkeleton; 1.1005 + } 1.1006 + setIntervalPattern(UCAL_YEAR, skeleton, bestSkeleton, differenceInfo, 1.1007 + &extendedSkeleton, &extendedBestSkeleton); 1.1008 + } else { 1.1009 + setIntervalPattern(UCAL_MINUTE, skeleton, bestSkeleton, differenceInfo); 1.1010 + setIntervalPattern(UCAL_HOUR, skeleton, bestSkeleton, differenceInfo); 1.1011 + setIntervalPattern(UCAL_AM_PM, skeleton, bestSkeleton, differenceInfo); 1.1012 + } 1.1013 + return true; 1.1014 +} 1.1015 + 1.1016 + 1.1017 + 1.1018 +void 1.1019 +DateIntervalFormat::setFallbackPattern(UCalendarDateFields field, 1.1020 + const UnicodeString& skeleton, 1.1021 + UErrorCode& status) { 1.1022 + if ( U_FAILURE(status) ) { 1.1023 + return; 1.1024 + } 1.1025 + UnicodeString pattern = fDtpng->getBestPattern(skeleton, status); 1.1026 + if ( U_FAILURE(status) ) { 1.1027 + return; 1.1028 + } 1.1029 + setPatternInfo(field, NULL, &pattern, fInfo->getDefaultOrder()); 1.1030 +} 1.1031 + 1.1032 + 1.1033 + 1.1034 + 1.1035 +void 1.1036 +DateIntervalFormat::setPatternInfo(UCalendarDateFields field, 1.1037 + const UnicodeString* firstPart, 1.1038 + const UnicodeString* secondPart, 1.1039 + UBool laterDateFirst) { 1.1040 + // for fall back interval patterns, 1.1041 + // the first part of the pattern is empty, 1.1042 + // the second part of the pattern is the full-pattern 1.1043 + // should be used in fall-back. 1.1044 + UErrorCode status = U_ZERO_ERROR; 1.1045 + // following should not set any wrong status. 1.1046 + int32_t itvPtnIndex = DateIntervalInfo::calendarFieldToIntervalIndex(field, 1.1047 + status); 1.1048 + if ( U_FAILURE(status) ) { 1.1049 + return; 1.1050 + } 1.1051 + PatternInfo& ptn = fIntervalPatterns[itvPtnIndex]; 1.1052 + if ( firstPart ) { 1.1053 + ptn.firstPart = *firstPart; 1.1054 + } 1.1055 + if ( secondPart ) { 1.1056 + ptn.secondPart = *secondPart; 1.1057 + } 1.1058 + ptn.laterDateFirst = laterDateFirst; 1.1059 +} 1.1060 + 1.1061 +void 1.1062 +DateIntervalFormat::setIntervalPattern(UCalendarDateFields field, 1.1063 + const UnicodeString& intervalPattern) { 1.1064 + UBool order = fInfo->getDefaultOrder(); 1.1065 + setIntervalPattern(field, intervalPattern, order); 1.1066 +} 1.1067 + 1.1068 + 1.1069 +void 1.1070 +DateIntervalFormat::setIntervalPattern(UCalendarDateFields field, 1.1071 + const UnicodeString& intervalPattern, 1.1072 + UBool laterDateFirst) { 1.1073 + const UnicodeString* pattern = &intervalPattern; 1.1074 + UBool order = laterDateFirst; 1.1075 + // check for "latestFirst:" or "earliestFirst:" prefix 1.1076 + int8_t prefixLength = sizeof(gLaterFirstPrefix)/sizeof(gLaterFirstPrefix[0]); 1.1077 + int8_t earliestFirstLength = sizeof(gEarlierFirstPrefix)/sizeof(gEarlierFirstPrefix[0]); 1.1078 + UnicodeString realPattern; 1.1079 + if ( intervalPattern.startsWith(gLaterFirstPrefix, prefixLength) ) { 1.1080 + order = true; 1.1081 + intervalPattern.extract(prefixLength, 1.1082 + intervalPattern.length() - prefixLength, 1.1083 + realPattern); 1.1084 + pattern = &realPattern; 1.1085 + } else if ( intervalPattern.startsWith(gEarlierFirstPrefix, 1.1086 + earliestFirstLength) ) { 1.1087 + order = false; 1.1088 + intervalPattern.extract(earliestFirstLength, 1.1089 + intervalPattern.length() - earliestFirstLength, 1.1090 + realPattern); 1.1091 + pattern = &realPattern; 1.1092 + } 1.1093 + 1.1094 + int32_t splitPoint = splitPatternInto2Part(*pattern); 1.1095 + 1.1096 + UnicodeString firstPart; 1.1097 + UnicodeString secondPart; 1.1098 + pattern->extract(0, splitPoint, firstPart); 1.1099 + if ( splitPoint < pattern->length() ) { 1.1100 + pattern->extract(splitPoint, pattern->length()-splitPoint, secondPart); 1.1101 + } 1.1102 + setPatternInfo(field, &firstPart, &secondPart, order); 1.1103 +} 1.1104 + 1.1105 + 1.1106 + 1.1107 + 1.1108 +/** 1.1109 + * Generate interval pattern from existing resource 1.1110 + * 1.1111 + * It not only save the interval patterns, 1.1112 + * but also return the extended skeleton and its best match skeleton. 1.1113 + * 1.1114 + * @param field largest different calendar field 1.1115 + * @param skeleton skeleton 1.1116 + * @param bestSkeleton the best match skeleton which has interval pattern 1.1117 + * defined in resource 1.1118 + * @param differenceInfo the difference between skeleton and best skeleton 1.1119 + * 0 means the best matched skeleton is the same as input skeleton 1.1120 + * 1 means the fields are the same, but field width are different 1.1121 + * 2 means the only difference between fields are v/z, 1.1122 + * -1 means there are other fields difference 1.1123 + * 1.1124 + * @param extendedSkeleton extended skeleton 1.1125 + * @param extendedBestSkeleton extended best match skeleton 1.1126 + * @return whether the interval pattern is found 1.1127 + * through extending skeleton or not. 1.1128 + * TRUE if interval pattern is found by 1.1129 + * extending skeleton, FALSE otherwise. 1.1130 + * @stable ICU 4.0 1.1131 + */ 1.1132 +UBool 1.1133 +DateIntervalFormat::setIntervalPattern(UCalendarDateFields field, 1.1134 + const UnicodeString* skeleton, 1.1135 + const UnicodeString* bestSkeleton, 1.1136 + int8_t differenceInfo, 1.1137 + UnicodeString* extendedSkeleton, 1.1138 + UnicodeString* extendedBestSkeleton) { 1.1139 + UErrorCode status = U_ZERO_ERROR; 1.1140 + // following getIntervalPattern() should not generate error status 1.1141 + UnicodeString pattern; 1.1142 + fInfo->getIntervalPattern(*bestSkeleton, field, pattern, status); 1.1143 + if ( pattern.isEmpty() ) { 1.1144 + // single date 1.1145 + if ( SimpleDateFormat::isFieldUnitIgnored(*bestSkeleton, field) ) { 1.1146 + // do nothing, format will handle it 1.1147 + return false; 1.1148 + } 1.1149 + 1.1150 + // for 24 hour system, interval patterns in resource file 1.1151 + // might not include pattern when am_pm differ, 1.1152 + // which should be the same as hour differ. 1.1153 + // add it here for simplicity 1.1154 + if ( field == UCAL_AM_PM ) { 1.1155 + fInfo->getIntervalPattern(*bestSkeleton, UCAL_HOUR, pattern,status); 1.1156 + if ( !pattern.isEmpty() ) { 1.1157 + setIntervalPattern(field, pattern); 1.1158 + } 1.1159 + return false; 1.1160 + } 1.1161 + // else, looking for pattern when 'y' differ for 'dMMMM' skeleton, 1.1162 + // first, get best match pattern "MMMd", 1.1163 + // since there is no pattern for 'y' differs for skeleton 'MMMd', 1.1164 + // need to look for it from skeleton 'yMMMd', 1.1165 + // if found, adjust field width in interval pattern from 1.1166 + // "MMM" to "MMMM". 1.1167 + UChar fieldLetter = fgCalendarFieldToPatternLetter[field]; 1.1168 + if ( extendedSkeleton ) { 1.1169 + *extendedSkeleton = *skeleton; 1.1170 + *extendedBestSkeleton = *bestSkeleton; 1.1171 + extendedSkeleton->insert(0, fieldLetter); 1.1172 + extendedBestSkeleton->insert(0, fieldLetter); 1.1173 + // for example, looking for patterns when 'y' differ for 1.1174 + // skeleton "MMMM". 1.1175 + fInfo->getIntervalPattern(*extendedBestSkeleton,field,pattern,status); 1.1176 + if ( pattern.isEmpty() && differenceInfo == 0 ) { 1.1177 + // if there is no skeleton "yMMMM" defined, 1.1178 + // look for the best match skeleton, for example: "yMMM" 1.1179 + const UnicodeString* tmpBest = fInfo->getBestSkeleton( 1.1180 + *extendedBestSkeleton, differenceInfo); 1.1181 + if ( tmpBest != 0 && differenceInfo != -1 ) { 1.1182 + fInfo->getIntervalPattern(*tmpBest, field, pattern, status); 1.1183 + bestSkeleton = tmpBest; 1.1184 + } 1.1185 + } 1.1186 + } 1.1187 + } 1.1188 + if ( !pattern.isEmpty() ) { 1.1189 + if ( differenceInfo != 0 ) { 1.1190 + UnicodeString adjustIntervalPattern; 1.1191 + adjustFieldWidth(*skeleton, *bestSkeleton, pattern, differenceInfo, 1.1192 + adjustIntervalPattern); 1.1193 + setIntervalPattern(field, adjustIntervalPattern); 1.1194 + } else { 1.1195 + setIntervalPattern(field, pattern); 1.1196 + } 1.1197 + if ( extendedSkeleton && !extendedSkeleton->isEmpty() ) { 1.1198 + return TRUE; 1.1199 + } 1.1200 + } 1.1201 + return FALSE; 1.1202 +} 1.1203 + 1.1204 + 1.1205 + 1.1206 +int32_t U_EXPORT2 1.1207 +DateIntervalFormat::splitPatternInto2Part(const UnicodeString& intervalPattern) { 1.1208 + UBool inQuote = false; 1.1209 + UChar prevCh = 0; 1.1210 + int32_t count = 0; 1.1211 + 1.1212 + /* repeatedPattern used to record whether a pattern has already seen. 1.1213 + It is a pattern applies to first calendar if it is first time seen, 1.1214 + otherwise, it is a pattern applies to the second calendar 1.1215 + */ 1.1216 + UBool patternRepeated[] = 1.1217 + { 1.1218 + // A B C D E F G H I J K L M N O 1.1219 + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1.1220 + // P Q R S T U V W X Y Z 1.1221 + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1.1222 + // a b c d e f g h i j k l m n o 1.1223 + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1.1224 + // p q r s t u v w x y z 1.1225 + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 1.1226 + }; 1.1227 + 1.1228 + int8_t PATTERN_CHAR_BASE = 0x41; 1.1229 + 1.1230 + /* loop through the pattern string character by character looking for 1.1231 + * the first repeated pattern letter, which breaks the interval pattern 1.1232 + * into 2 parts. 1.1233 + */ 1.1234 + int32_t i; 1.1235 + UBool foundRepetition = false; 1.1236 + for (i = 0; i < intervalPattern.length(); ++i) { 1.1237 + UChar ch = intervalPattern.charAt(i); 1.1238 + 1.1239 + if (ch != prevCh && count > 0) { 1.1240 + // check the repeativeness of pattern letter 1.1241 + UBool repeated = patternRepeated[(int)(prevCh - PATTERN_CHAR_BASE)]; 1.1242 + if ( repeated == FALSE ) { 1.1243 + patternRepeated[prevCh - PATTERN_CHAR_BASE] = TRUE; 1.1244 + } else { 1.1245 + foundRepetition = true; 1.1246 + break; 1.1247 + } 1.1248 + count = 0; 1.1249 + } 1.1250 + if (ch == '\'') { 1.1251 + // Consecutive single quotes are a single quote literal, 1.1252 + // either outside of quotes or between quotes 1.1253 + if ((i+1) < intervalPattern.length() && 1.1254 + intervalPattern.charAt(i+1) == '\'') { 1.1255 + ++i; 1.1256 + } else { 1.1257 + inQuote = ! inQuote; 1.1258 + } 1.1259 + } 1.1260 + else if (!inQuote && ((ch >= 0x0061 /*'a'*/ && ch <= 0x007A /*'z'*/) 1.1261 + || (ch >= 0x0041 /*'A'*/ && ch <= 0x005A /*'Z'*/))) { 1.1262 + // ch is a date-time pattern character 1.1263 + prevCh = ch; 1.1264 + ++count; 1.1265 + } 1.1266 + } 1.1267 + // check last pattern char, distinguish 1.1268 + // "dd MM" ( no repetition ), 1.1269 + // "d-d"(last char repeated ), and 1.1270 + // "d-d MM" ( repetition found ) 1.1271 + if ( count > 0 && foundRepetition == FALSE ) { 1.1272 + if ( patternRepeated[(int)(prevCh - PATTERN_CHAR_BASE)] == FALSE ) { 1.1273 + count = 0; 1.1274 + } 1.1275 + } 1.1276 + return (i - count); 1.1277 +} 1.1278 + 1.1279 + 1.1280 + 1.1281 +UnicodeString& 1.1282 +DateIntervalFormat::fallbackFormat(Calendar& fromCalendar, 1.1283 + Calendar& toCalendar, 1.1284 + UnicodeString& appendTo, 1.1285 + FieldPosition& pos, 1.1286 + UErrorCode& status) const { 1.1287 + if ( U_FAILURE(status) ) { 1.1288 + return appendTo; 1.1289 + } 1.1290 + // the fall back 1.1291 + // no need delete earlierDate and laterDate since they are adopted 1.1292 + UnicodeString* earlierDate = new UnicodeString(); 1.1293 + *earlierDate = fDateFormat->format(fromCalendar, *earlierDate, pos); 1.1294 + UnicodeString* laterDate = new UnicodeString(); 1.1295 + *laterDate = fDateFormat->format(toCalendar, *laterDate, pos); 1.1296 + UnicodeString fallbackPattern; 1.1297 + fInfo->getFallbackIntervalPattern(fallbackPattern); 1.1298 + Formattable fmtArray[2]; 1.1299 + fmtArray[0].adoptString(earlierDate); 1.1300 + fmtArray[1].adoptString(laterDate); 1.1301 + 1.1302 + UnicodeString fallback; 1.1303 + MessageFormat::format(fallbackPattern, fmtArray, 2, fallback, status); 1.1304 + if ( U_SUCCESS(status) ) { 1.1305 + appendTo.append(fallback); 1.1306 + } 1.1307 + return appendTo; 1.1308 +} 1.1309 + 1.1310 + 1.1311 + 1.1312 + 1.1313 +UBool U_EXPORT2 1.1314 +DateIntervalFormat::fieldExistsInSkeleton(UCalendarDateFields field, 1.1315 + const UnicodeString& skeleton) 1.1316 +{ 1.1317 + const UChar fieldChar = fgCalendarFieldToPatternLetter[field]; 1.1318 + return ( (skeleton.indexOf(fieldChar) == -1)?FALSE:TRUE ) ; 1.1319 +} 1.1320 + 1.1321 + 1.1322 + 1.1323 +void U_EXPORT2 1.1324 +DateIntervalFormat::adjustFieldWidth(const UnicodeString& inputSkeleton, 1.1325 + const UnicodeString& bestMatchSkeleton, 1.1326 + const UnicodeString& bestIntervalPattern, 1.1327 + int8_t differenceInfo, 1.1328 + UnicodeString& adjustedPtn) { 1.1329 + adjustedPtn = bestIntervalPattern; 1.1330 + int32_t inputSkeletonFieldWidth[] = 1.1331 + { 1.1332 + // A B C D E F G H I J K L M N O 1.1333 + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1.1334 + // P Q R S T U V W X Y Z 1.1335 + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1.1336 + // a b c d e f g h i j k l m n o 1.1337 + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1.1338 + // p q r s t u v w x y z 1.1339 + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 1.1340 + }; 1.1341 + 1.1342 + int32_t bestMatchSkeletonFieldWidth[] = 1.1343 + { 1.1344 + // A B C D E F G H I J K L M N O 1.1345 + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1.1346 + // P Q R S T U V W X Y Z 1.1347 + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1.1348 + // a b c d e f g h i j k l m n o 1.1349 + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1.1350 + // p q r s t u v w x y z 1.1351 + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 1.1352 + }; 1.1353 + 1.1354 + DateIntervalInfo::parseSkeleton(inputSkeleton, inputSkeletonFieldWidth); 1.1355 + DateIntervalInfo::parseSkeleton(bestMatchSkeleton, bestMatchSkeletonFieldWidth); 1.1356 + if ( differenceInfo == 2 ) { 1.1357 + adjustedPtn.findAndReplace(UnicodeString((UChar)0x76 /* v */), 1.1358 + UnicodeString((UChar)0x7a /* z */)); 1.1359 + } 1.1360 + 1.1361 + UBool inQuote = false; 1.1362 + UChar prevCh = 0; 1.1363 + int32_t count = 0; 1.1364 + 1.1365 + const int8_t PATTERN_CHAR_BASE = 0x41; 1.1366 + 1.1367 + // loop through the pattern string character by character 1.1368 + int32_t adjustedPtnLength = adjustedPtn.length(); 1.1369 + int32_t i; 1.1370 + for (i = 0; i < adjustedPtnLength; ++i) { 1.1371 + UChar ch = adjustedPtn.charAt(i); 1.1372 + if (ch != prevCh && count > 0) { 1.1373 + // check the repeativeness of pattern letter 1.1374 + UChar skeletonChar = prevCh; 1.1375 + if ( skeletonChar == CAP_L ) { 1.1376 + // there is no "L" (always be "M") in skeleton, 1.1377 + // but there is "L" in pattern. 1.1378 + // for skeleton "M+", the pattern might be "...L..." 1.1379 + skeletonChar = CAP_M; 1.1380 + } 1.1381 + int32_t fieldCount = bestMatchSkeletonFieldWidth[(int)(skeletonChar - PATTERN_CHAR_BASE)]; 1.1382 + int32_t inputFieldCount = inputSkeletonFieldWidth[(int)(skeletonChar - PATTERN_CHAR_BASE)]; 1.1383 + if ( fieldCount == count && inputFieldCount > fieldCount ) { 1.1384 + count = inputFieldCount - fieldCount; 1.1385 + int32_t j; 1.1386 + for ( j = 0; j < count; ++j ) { 1.1387 + adjustedPtn.insert(i, prevCh); 1.1388 + } 1.1389 + i += count; 1.1390 + adjustedPtnLength += count; 1.1391 + } 1.1392 + count = 0; 1.1393 + } 1.1394 + if (ch == '\'') { 1.1395 + // Consecutive single quotes are a single quote literal, 1.1396 + // either outside of quotes or between quotes 1.1397 + if ((i+1) < adjustedPtn.length() && adjustedPtn.charAt(i+1) == '\'') { 1.1398 + ++i; 1.1399 + } else { 1.1400 + inQuote = ! inQuote; 1.1401 + } 1.1402 + } 1.1403 + else if ( ! inQuote && ((ch >= 0x0061 /*'a'*/ && ch <= 0x007A /*'z'*/) 1.1404 + || (ch >= 0x0041 /*'A'*/ && ch <= 0x005A /*'Z'*/))) { 1.1405 + // ch is a date-time pattern character 1.1406 + prevCh = ch; 1.1407 + ++count; 1.1408 + } 1.1409 + } 1.1410 + if ( count > 0 ) { 1.1411 + // last item 1.1412 + // check the repeativeness of pattern letter 1.1413 + UChar skeletonChar = prevCh; 1.1414 + if ( skeletonChar == CAP_L ) { 1.1415 + // there is no "L" (always be "M") in skeleton, 1.1416 + // but there is "L" in pattern. 1.1417 + // for skeleton "M+", the pattern might be "...L..." 1.1418 + skeletonChar = CAP_M; 1.1419 + } 1.1420 + int32_t fieldCount = bestMatchSkeletonFieldWidth[(int)(skeletonChar - PATTERN_CHAR_BASE)]; 1.1421 + int32_t inputFieldCount = inputSkeletonFieldWidth[(int)(skeletonChar - PATTERN_CHAR_BASE)]; 1.1422 + if ( fieldCount == count && inputFieldCount > fieldCount ) { 1.1423 + count = inputFieldCount - fieldCount; 1.1424 + int32_t j; 1.1425 + for ( j = 0; j < count; ++j ) { 1.1426 + adjustedPtn.append(prevCh); 1.1427 + } 1.1428 + } 1.1429 + } 1.1430 +} 1.1431 + 1.1432 + 1.1433 + 1.1434 +void 1.1435 +DateIntervalFormat::concatSingleDate2TimeInterval(const UChar* format, 1.1436 + int32_t formatLen, 1.1437 + const UnicodeString& datePattern, 1.1438 + UCalendarDateFields field, 1.1439 + UErrorCode& status) { 1.1440 + // following should not set wrong status 1.1441 + int32_t itvPtnIndex = DateIntervalInfo::calendarFieldToIntervalIndex(field, 1.1442 + status); 1.1443 + if ( U_FAILURE(status) ) { 1.1444 + return; 1.1445 + } 1.1446 + PatternInfo& timeItvPtnInfo = fIntervalPatterns[itvPtnIndex]; 1.1447 + if ( !timeItvPtnInfo.firstPart.isEmpty() ) { 1.1448 + // UnicodeString allocated here is adopted, so no need to delete 1.1449 + UnicodeString* timeIntervalPattern = new UnicodeString(timeItvPtnInfo.firstPart); 1.1450 + timeIntervalPattern->append(timeItvPtnInfo.secondPart); 1.1451 + UnicodeString* dateStr = new UnicodeString(datePattern); 1.1452 + Formattable fmtArray[2]; 1.1453 + fmtArray[0].adoptString(timeIntervalPattern); 1.1454 + fmtArray[1].adoptString(dateStr); 1.1455 + UnicodeString combinedPattern; 1.1456 + MessageFormat::format(UnicodeString(TRUE, format, formatLen), 1.1457 + fmtArray, 2, combinedPattern, status); 1.1458 + if ( U_FAILURE(status) ) { 1.1459 + return; 1.1460 + } 1.1461 + setIntervalPattern(field, combinedPattern, timeItvPtnInfo.laterDateFirst); 1.1462 + } 1.1463 + // else: fall back 1.1464 + // it should not happen if the interval format defined is valid 1.1465 +} 1.1466 + 1.1467 + 1.1468 + 1.1469 +const UChar 1.1470 +DateIntervalFormat::fgCalendarFieldToPatternLetter[] = 1.1471 +{ 1.1472 + /*GyM*/ CAP_G, LOW_Y, CAP_M, 1.1473 + /*wWd*/ LOW_W, CAP_W, LOW_D, 1.1474 + /*DEF*/ CAP_D, CAP_E, CAP_F, 1.1475 + /*ahH*/ LOW_A, LOW_H, CAP_H, 1.1476 + /*m..*/ LOW_M, 1.1477 +}; 1.1478 + 1.1479 + 1.1480 +U_NAMESPACE_END 1.1481 + 1.1482 +#endif