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