1 /******************************************************************************* 2 * Copyright (C) 2008-2013, International Business Machines Corporation and 3 * others. All Rights Reserved. 4 ******************************************************************************* 5 * 6 * File DTITVFMT.CPP 7 * 8 ******************************************************************************* 9 */ 10 11 #include "utypeinfo.h" // 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, LOW_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*>(formatObj); 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, status); 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 void 420 DateIntervalFormat::adoptTimeZone(TimeZone* zone) 421 { 422 if (fDateFormat != NULL) { 423 fDateFormat->adoptTimeZone(zone); 424 } 425 // The fDateFormat has the master calendar for the DateIntervalFormat and has 426 // ownership of any adopted TimeZone; fFromCalendar and fToCalendar are internal 427 // work clones of that calendar (and should not also be given ownership of the 428 // adopted TimeZone). 429 if (fFromCalendar) { 430 fFromCalendar->setTimeZone(*zone); 431 } 432 if (fToCalendar) { 433 fToCalendar->setTimeZone(*zone); 434 } 435 } 436 437 void 438 DateIntervalFormat::setTimeZone(const TimeZone& zone) 439 { 440 if (fDateFormat != NULL) { 441 fDateFormat->setTimeZone(zone); 442 } 443 // The fDateFormat has the master calendar for the DateIntervalFormat; 444 // fFromCalendar and fToCalendar are internal work clones of that calendar. 445 if (fFromCalendar) { 446 fFromCalendar->setTimeZone(zone); 447 } 448 if (fToCalendar) { 449 fToCalendar->setTimeZone(zone); 450 } 451 } 452 453 const TimeZone& 454 DateIntervalFormat::getTimeZone() const 455 { 456 if (fDateFormat != NULL) { 457 return fDateFormat->getTimeZone(); 458 } 459 // If fDateFormat is NULL (unexpected), create default timezone. 460 return *(TimeZone::createDefault()); 461 } 462 463 DateIntervalFormat::DateIntervalFormat(const Locale& locale, 464 DateIntervalInfo* dtItvInfo, 465 const UnicodeString* skeleton, 466 UErrorCode& status) 467 : fInfo(NULL), 468 fDateFormat(NULL), 469 fFromCalendar(NULL), 470 fToCalendar(NULL), 471 fDtpng(NULL) 472 { 473 if ( U_FAILURE(status) ) { 474 delete dtItvInfo; 475 return; 476 } 477 fDtpng = DateTimePatternGenerator::createInstance(locale, status); 478 SimpleDateFormat* dtfmt = createSDFPatternInstance(*skeleton, locale, 479 fDtpng, status); 480 if ( U_FAILURE(status) ) { 481 delete dtItvInfo; 482 delete fDtpng; 483 delete dtfmt; 484 return; 485 } 486 if ( dtfmt == NULL || dtItvInfo == NULL || fDtpng == NULL ) { 487 status = U_MEMORY_ALLOCATION_ERROR; 488 // safe to delete NULL 489 delete dtfmt; 490 delete dtItvInfo; 491 delete fDtpng; 492 return; 493 } 494 if ( skeleton ) { 495 fSkeleton = *skeleton; 496 } 497 fInfo = dtItvInfo; 498 fDateFormat = dtfmt; 499 if ( dtfmt->getCalendar() ) { 500 fFromCalendar = dtfmt->getCalendar()->clone(); 501 fToCalendar = dtfmt->getCalendar()->clone(); 502 } else { 503 fFromCalendar = NULL; 504 fToCalendar = NULL; 505 } 506 initializePattern(status); 507 } 508 509 510 SimpleDateFormat* U_EXPORT2 511 DateIntervalFormat::createSDFPatternInstance(const UnicodeString& skeleton, 512 const Locale& locale, 513 DateTimePatternGenerator* dtpng, 514 UErrorCode& status) 515 { 516 if ( U_FAILURE(status) ) { 517 return NULL; 518 } 519 520 const UnicodeString pattern = dtpng->getBestPattern(skeleton, status); 521 if ( U_FAILURE(status) ) { 522 return NULL; 523 } 524 SimpleDateFormat* dtfmt = new SimpleDateFormat(pattern, locale, status); 525 if ( U_FAILURE(status) ) { 526 delete dtfmt; 527 return NULL; 528 } 529 return dtfmt; 530 } 531 532 533 DateIntervalFormat* U_EXPORT2 534 DateIntervalFormat::create(const Locale& locale, 535 DateIntervalInfo* dtitvinf, 536 const UnicodeString* skeleton, 537 UErrorCode& status) { 538 DateIntervalFormat* f = new DateIntervalFormat(locale, dtitvinf, 539 skeleton, status); 540 if ( f == NULL ) { 541 status = U_MEMORY_ALLOCATION_ERROR; 542 delete dtitvinf; 543 } else if ( U_FAILURE(status) ) { 544 // safe to delete f, although nothing acutally is saved 545 delete f; 546 f = 0; 547 } 548 return f; 549 } 550 551 552 553 /** 554 * Initialize interval patterns locale to this formatter 555 * 556 * This code is a bit complicated since 557 * 1. the interval patterns saved in resource bundle files are interval 558 * patterns based on date or time only. 559 * It does not have interval patterns based on both date and time. 560 * Interval patterns on both date and time are algorithm generated. 561 * 562 * For example, it has interval patterns on skeleton "dMy" and "hm", 563 * but it does not have interval patterns on skeleton "dMyhm". 564 * 565 * The rule to genearte interval patterns for both date and time skeleton are 566 * 1) when the year, month, or day differs, concatenate the two original 567 * expressions with a separator between, 568 * For example, interval pattern from "Jan 10, 2007 10:10 am" 569 * to "Jan 11, 2007 10:10am" is 570 * "Jan 10, 2007 10:10 am - Jan 11, 2007 10:10am" 571 * 572 * 2) otherwise, present the date followed by the range expression 573 * for the time. 574 * For example, interval pattern from "Jan 10, 2007 10:10 am" 575 * to "Jan 10, 2007 11:10am" is 576 * "Jan 10, 2007 10:10 am - 11:10am" 577 * 578 * 2. even a pattern does not request a certion calendar field, 579 * the interval pattern needs to include such field if such fields are 580 * different between 2 dates. 581 * For example, a pattern/skeleton is "hm", but the interval pattern 582 * includes year, month, and date when year, month, and date differs. 583 * 584 * @param status output param set to success/failure code on exit 585 * @stable ICU 4.0 586 */ 587 void 588 DateIntervalFormat::initializePattern(UErrorCode& status) { 589 if ( U_FAILURE(status) ) { 590 return; 591 } 592 const Locale& locale = fDateFormat->getSmpFmtLocale(); 593 if ( fSkeleton.isEmpty() ) { 594 UnicodeString fullPattern; 595 fDateFormat->toPattern(fullPattern); 596 #ifdef DTITVFMT_DEBUG 597 char result[1000]; 598 char result_1[1000]; 599 char mesg[2000]; 600 fSkeleton.extract(0, fSkeleton.length(), result, "UTF-8"); 601 sprintf(mesg, "in getBestSkeleton: fSkeleton: %s; \n", result); 602 PRINTMESG(mesg) 603 #endif 604 // fSkeleton is already set by createDateIntervalInstance() 605 // or by createInstance(UnicodeString skeleton, .... ) 606 fSkeleton = fDtpng->getSkeleton(fullPattern, status); 607 if ( U_FAILURE(status) ) { 608 return; 609 } 610 } 611 612 // initialize the fIntervalPattern ordering 613 int8_t i; 614 for ( i = 0; i < DateIntervalInfo::kIPI_MAX_INDEX; ++i ) { 615 fIntervalPatterns[i].laterDateFirst = fInfo->getDefaultOrder(); 616 } 617 618 /* Check whether the skeleton is a combination of date and time. 619 * For the complication reason 1 explained above. 620 */ 621 UnicodeString dateSkeleton; 622 UnicodeString timeSkeleton; 623 UnicodeString normalizedTimeSkeleton; 624 UnicodeString normalizedDateSkeleton; 625 626 627 /* the difference between time skeleton and normalizedTimeSkeleton are: 628 * 1. (Formerly, normalized time skeleton folded 'H' to 'h'; no longer true) 629 * 2. 'a' is omitted in normalized time skeleton. 630 * 3. there is only one appearance for 'h' or 'H', 'm','v', 'z' in normalized 631 * time skeleton 632 * 633 * The difference between date skeleton and normalizedDateSkeleton are: 634 * 1. both 'y' and 'd' appear only once in normalizeDateSkeleton 635 * 2. 'E' and 'EE' are normalized into 'EEE' 636 * 3. 'MM' is normalized into 'M' 637 */ 638 getDateTimeSkeleton(fSkeleton, dateSkeleton, normalizedDateSkeleton, 639 timeSkeleton, normalizedTimeSkeleton); 640 641 #ifdef DTITVFMT_DEBUG 642 char result[1000]; 643 char result_1[1000]; 644 char mesg[2000]; 645 fSkeleton.extract(0, fSkeleton.length(), result, "UTF-8"); 646 sprintf(mesg, "in getBestSkeleton: fSkeleton: %s; \n", result); 647 PRINTMESG(mesg) 648 #endif 649 650 651 UBool found = setSeparateDateTimePtn(normalizedDateSkeleton, 652 normalizedTimeSkeleton); 653 654 if ( found == false ) { 655 // use fallback 656 // TODO: if user asks "m"(minute), but "d"(day) differ 657 if ( timeSkeleton.length() != 0 ) { 658 if ( dateSkeleton.length() == 0 ) { 659 // prefix with yMd 660 timeSkeleton.insert(0, gDateFormatSkeleton[DateFormat::kShort], -1); 661 UnicodeString pattern = fDtpng->getBestPattern(timeSkeleton, status); 662 if ( U_FAILURE(status) ) { 663 return; 664 } 665 // for fall back interval patterns, 666 // the first part of the pattern is empty, 667 // the second part of the pattern is the full-pattern 668 // should be used in fall-back. 669 setPatternInfo(UCAL_DATE, NULL, &pattern, fInfo->getDefaultOrder()); 670 setPatternInfo(UCAL_MONTH, NULL, &pattern, fInfo->getDefaultOrder()); 671 setPatternInfo(UCAL_YEAR, NULL, &pattern, fInfo->getDefaultOrder()); 672 } else { 673 // TODO: fall back 674 } 675 } else { 676 // TODO: fall back 677 } 678 return; 679 } // end of skeleton not found 680 // interval patterns for skeleton are found in resource 681 if ( timeSkeleton.length() == 0 ) { 682 // done 683 } else if ( dateSkeleton.length() == 0 ) { 684 // prefix with yMd 685 timeSkeleton.insert(0, gDateFormatSkeleton[DateFormat::kShort], -1); 686 UnicodeString pattern = fDtpng->getBestPattern(timeSkeleton, status); 687 if ( U_FAILURE(status) ) { 688 return; 689 } 690 // for fall back interval patterns, 691 // the first part of the pattern is empty, 692 // the second part of the pattern is the full-pattern 693 // should be used in fall-back. 694 setPatternInfo(UCAL_DATE, NULL, &pattern, fInfo->getDefaultOrder()); 695 setPatternInfo(UCAL_MONTH, NULL, &pattern, fInfo->getDefaultOrder()); 696 setPatternInfo(UCAL_YEAR, NULL, &pattern, fInfo->getDefaultOrder()); 697 } else { 698 /* if both present, 699 * 1) when the year, month, or day differs, 700 * concatenate the two original expressions with a separator between, 701 * 2) otherwise, present the date followed by the 702 * range expression for the time. 703 */ 704 /* 705 * 1) when the year, month, or day differs, 706 * concatenate the two original expressions with a separator between, 707 */ 708 // if field exists, use fall back 709 UnicodeString skeleton = fSkeleton; 710 if ( !fieldExistsInSkeleton(UCAL_DATE, dateSkeleton) ) { 711 // prefix skeleton with 'd' 712 skeleton.insert(0, LOW_D); 713 setFallbackPattern(UCAL_DATE, skeleton, status); 714 } 715 if ( !fieldExistsInSkeleton(UCAL_MONTH, dateSkeleton) ) { 716 // then prefix skeleton with 'M' 717 skeleton.insert(0, CAP_M); 718 setFallbackPattern(UCAL_MONTH, skeleton, status); 719 } 720 if ( !fieldExistsInSkeleton(UCAL_YEAR, dateSkeleton) ) { 721 // then prefix skeleton with 'y' 722 skeleton.insert(0, LOW_Y); 723 setFallbackPattern(UCAL_YEAR, skeleton, status); 724 } 725 726 /* 727 * 2) otherwise, present the date followed by the 728 * range expression for the time. 729 */ 730 // Need the Date/Time pattern for concatnation the date with 731 // the time interval. 732 // The date/time pattern ( such as {0} {1} ) is saved in 733 // calendar, that is why need to get the CalendarData here. 734 CalendarData* calData = new CalendarData(locale, NULL, status); 735 736 if ( U_FAILURE(status) ) { 737 delete calData; 738 return; 739 } 740 741 if ( calData == NULL ) { 742 status = U_MEMORY_ALLOCATION_ERROR; 743 return; 744 } 745 746 const UResourceBundle* dateTimePatternsRes = calData->getByKey( 747 gDateTimePatternsTag, status); 748 int32_t dateTimeFormatLength; 749 const UChar* dateTimeFormat = ures_getStringByIndex( 750 dateTimePatternsRes, 751 (int32_t)DateFormat::kDateTime, 752 &dateTimeFormatLength, &status); 753 if ( U_FAILURE(status) ) { 754 return; 755 } 756 757 UnicodeString datePattern = fDtpng->getBestPattern(dateSkeleton, status); 758 759 concatSingleDate2TimeInterval(dateTimeFormat, dateTimeFormatLength, 760 datePattern, UCAL_AM_PM, status); 761 concatSingleDate2TimeInterval(dateTimeFormat, dateTimeFormatLength, 762 datePattern, UCAL_HOUR, status); 763 concatSingleDate2TimeInterval(dateTimeFormat, dateTimeFormatLength, 764 datePattern, UCAL_MINUTE, status); 765 delete calData; 766 } 767 } 768 769 770 771 void U_EXPORT2 772 DateIntervalFormat::getDateTimeSkeleton(const UnicodeString& skeleton, 773 UnicodeString& dateSkeleton, 774 UnicodeString& normalizedDateSkeleton, 775 UnicodeString& timeSkeleton, 776 UnicodeString& normalizedTimeSkeleton) { 777 // dateSkeleton follows the sequence of y*M*E*d* 778 // timeSkeleton follows the sequence of hm*[v|z]? 779 int32_t ECount = 0; 780 int32_t dCount = 0; 781 int32_t MCount = 0; 782 int32_t yCount = 0; 783 int32_t hCount = 0; 784 int32_t HCount = 0; 785 int32_t mCount = 0; 786 int32_t vCount = 0; 787 int32_t zCount = 0; 788 int32_t i; 789 790 for (i = 0; i < skeleton.length(); ++i) { 791 UChar ch = skeleton[i]; 792 switch ( ch ) { 793 case CAP_E: 794 dateSkeleton.append(ch); 795 ++ECount; 796 break; 797 case LOW_D: 798 dateSkeleton.append(ch); 799 ++dCount; 800 break; 801 case CAP_M: 802 dateSkeleton.append(ch); 803 ++MCount; 804 break; 805 case LOW_Y: 806 dateSkeleton.append(ch); 807 ++yCount; 808 break; 809 case CAP_G: 810 case CAP_Y: 811 case LOW_U: 812 case CAP_Q: 813 case LOW_Q: 814 case CAP_L: 815 case LOW_L: 816 case CAP_W: 817 case LOW_W: 818 case CAP_D: 819 case CAP_F: 820 case LOW_G: 821 case LOW_E: 822 case LOW_C: 823 normalizedDateSkeleton.append(ch); 824 dateSkeleton.append(ch); 825 break; 826 case LOW_A: 827 // 'a' is implicitly handled 828 timeSkeleton.append(ch); 829 break; 830 case LOW_H: 831 timeSkeleton.append(ch); 832 ++hCount; 833 break; 834 case CAP_H: 835 timeSkeleton.append(ch); 836 ++HCount; 837 break; 838 case LOW_M: 839 timeSkeleton.append(ch); 840 ++mCount; 841 break; 842 case LOW_Z: 843 ++zCount; 844 timeSkeleton.append(ch); 845 break; 846 case LOW_V: 847 ++vCount; 848 timeSkeleton.append(ch); 849 break; 850 case CAP_V: 851 case CAP_Z: 852 case LOW_K: 853 case CAP_K: 854 case LOW_J: 855 case LOW_S: 856 case CAP_S: 857 case CAP_A: 858 timeSkeleton.append(ch); 859 normalizedTimeSkeleton.append(ch); 860 break; 861 } 862 } 863 864 /* generate normalized form for date*/ 865 if ( yCount != 0 ) { 866 for (i = 0; i < yCount; ++i) { 867 normalizedDateSkeleton.append(LOW_Y); 868 } 869 } 870 if ( MCount != 0 ) { 871 if ( MCount < 3 ) { 872 normalizedDateSkeleton.append(CAP_M); 873 } else { 874 int32_t i; 875 for ( i = 0; i < MCount && i < MAX_M_COUNT; ++i ) { 876 normalizedDateSkeleton.append(CAP_M); 877 } 878 } 879 } 880 if ( ECount != 0 ) { 881 if ( ECount <= 3 ) { 882 normalizedDateSkeleton.append(CAP_E); 883 } else { 884 int32_t i; 885 for ( i = 0; i < ECount && i < MAX_E_COUNT; ++i ) { 886 normalizedDateSkeleton.append(CAP_E); 887 } 888 } 889 } 890 if ( dCount != 0 ) { 891 normalizedDateSkeleton.append(LOW_D); 892 } 893 894 /* generate normalized form for time */ 895 if ( HCount != 0 ) { 896 normalizedTimeSkeleton.append(CAP_H); 897 } 898 else if ( hCount != 0 ) { 899 normalizedTimeSkeleton.append(LOW_H); 900 } 901 if ( mCount != 0 ) { 902 normalizedTimeSkeleton.append(LOW_M); 903 } 904 if ( zCount != 0 ) { 905 normalizedTimeSkeleton.append(LOW_Z); 906 } 907 if ( vCount != 0 ) { 908 normalizedTimeSkeleton.append(LOW_V); 909 } 910 } 911 912 913 /** 914 * Generate date or time interval pattern from resource, 915 * and set them into the interval pattern locale to this formatter. 916 * 917 * It needs to handle the following: 918 * 1. need to adjust field width. 919 * For example, the interval patterns saved in DateIntervalInfo 920 * includes "dMMMy", but not "dMMMMy". 921 * Need to get interval patterns for dMMMMy from dMMMy. 922 * Another example, the interval patterns saved in DateIntervalInfo 923 * includes "hmv", but not "hmz". 924 * Need to get interval patterns for "hmz' from 'hmv' 925 * 926 * 2. there might be no pattern for 'y' differ for skeleton "Md", 927 * in order to get interval patterns for 'y' differ, 928 * need to look for it from skeleton 'yMd' 929 * 930 * @param dateSkeleton normalized date skeleton 931 * @param timeSkeleton normalized time skeleton 932 * @return whether the resource is found for the skeleton. 933 * TRUE if interval pattern found for the skeleton, 934 * FALSE otherwise. 935 * @stable ICU 4.0 936 */ 937 UBool 938 DateIntervalFormat::setSeparateDateTimePtn( 939 const UnicodeString& dateSkeleton, 940 const UnicodeString& timeSkeleton) { 941 const UnicodeString* skeleton; 942 // if both date and time skeleton present, 943 // the final interval pattern might include time interval patterns 944 // ( when, am_pm, hour, minute differ ), 945 // but not date interval patterns ( when year, month, day differ ). 946 // For year/month/day differ, it falls back to fall-back pattern. 947 if ( timeSkeleton.length() != 0 ) { 948 skeleton = &timeSkeleton; 949 } else { 950 skeleton = &dateSkeleton; 951 } 952 953 /* interval patterns for skeleton "dMMMy" (but not "dMMMMy") 954 * are defined in resource, 955 * interval patterns for skeleton "dMMMMy" are calculated by 956 * 1. get the best match skeleton for "dMMMMy", which is "dMMMy" 957 * 2. get the interval patterns for "dMMMy", 958 * 3. extend "MMM" to "MMMM" in above interval patterns for "dMMMMy" 959 * getBestSkeleton() is step 1. 960 */ 961 // best skeleton, and the difference information 962 int8_t differenceInfo = 0; 963 const UnicodeString* bestSkeleton = fInfo->getBestSkeleton(*skeleton, 964 differenceInfo); 965 /* best skeleton could be NULL. 966 For example: in "ca" resource file, 967 interval format is defined as following 968 intervalFormats{ 969 fallback{"{0} - {1}"} 970 } 971 there is no skeletons/interval patterns defined, 972 and the best skeleton match could be NULL 973 */ 974 if ( bestSkeleton == NULL ) { 975 return false; 976 } 977 978 // difference: 979 // 0 means the best matched skeleton is the same as input skeleton 980 // 1 means the fields are the same, but field width are different 981 // 2 means the only difference between fields are v/z, 982 // -1 means there are other fields difference 983 if ( differenceInfo == -1 ) { 984 // skeleton has different fields, not only v/z difference 985 return false; 986 } 987 988 if ( timeSkeleton.length() == 0 ) { 989 UnicodeString extendedSkeleton; 990 UnicodeString extendedBestSkeleton; 991 // only has date skeleton 992 setIntervalPattern(UCAL_DATE, skeleton, bestSkeleton, differenceInfo, 993 &extendedSkeleton, &extendedBestSkeleton); 994 995 UBool extended = setIntervalPattern(UCAL_MONTH, skeleton, bestSkeleton, 996 differenceInfo, 997 &extendedSkeleton, &extendedBestSkeleton); 998 999 if ( extended ) { 1000 bestSkeleton = &extendedBestSkeleton; 1001 skeleton = &extendedSkeleton; 1002 } 1003 setIntervalPattern(UCAL_YEAR, skeleton, bestSkeleton, differenceInfo, 1004 &extendedSkeleton, &extendedBestSkeleton); 1005 } else { 1006 setIntervalPattern(UCAL_MINUTE, skeleton, bestSkeleton, differenceInfo); 1007 setIntervalPattern(UCAL_HOUR, skeleton, bestSkeleton, differenceInfo); 1008 setIntervalPattern(UCAL_AM_PM, skeleton, bestSkeleton, differenceInfo); 1009 } 1010 return true; 1011 } 1012 1013 1014 1015 void 1016 DateIntervalFormat::setFallbackPattern(UCalendarDateFields field, 1017 const UnicodeString& skeleton, 1018 UErrorCode& status) { 1019 if ( U_FAILURE(status) ) { 1020 return; 1021 } 1022 UnicodeString pattern = fDtpng->getBestPattern(skeleton, status); 1023 if ( U_FAILURE(status) ) { 1024 return; 1025 } 1026 setPatternInfo(field, NULL, &pattern, fInfo->getDefaultOrder()); 1027 } 1028 1029 1030 1031 1032 void 1033 DateIntervalFormat::setPatternInfo(UCalendarDateFields field, 1034 const UnicodeString* firstPart, 1035 const UnicodeString* secondPart, 1036 UBool laterDateFirst) { 1037 // for fall back interval patterns, 1038 // the first part of the pattern is empty, 1039 // the second part of the pattern is the full-pattern 1040 // should be used in fall-back. 1041 UErrorCode status = U_ZERO_ERROR; 1042 // following should not set any wrong status. 1043 int32_t itvPtnIndex = DateIntervalInfo::calendarFieldToIntervalIndex(field, 1044 status); 1045 if ( U_FAILURE(status) ) { 1046 return; 1047 } 1048 PatternInfo& ptn = fIntervalPatterns[itvPtnIndex]; 1049 if ( firstPart ) { 1050 ptn.firstPart = *firstPart; 1051 } 1052 if ( secondPart ) { 1053 ptn.secondPart = *secondPart; 1054 } 1055 ptn.laterDateFirst = laterDateFirst; 1056 } 1057 1058 void 1059 DateIntervalFormat::setIntervalPattern(UCalendarDateFields field, 1060 const UnicodeString& intervalPattern) { 1061 UBool order = fInfo->getDefaultOrder(); 1062 setIntervalPattern(field, intervalPattern, order); 1063 } 1064 1065 1066 void 1067 DateIntervalFormat::setIntervalPattern(UCalendarDateFields field, 1068 const UnicodeString& intervalPattern, 1069 UBool laterDateFirst) { 1070 const UnicodeString* pattern = &intervalPattern; 1071 UBool order = laterDateFirst; 1072 // check for "latestFirst:" or "earliestFirst:" prefix 1073 int8_t prefixLength = sizeof(gLaterFirstPrefix)/sizeof(gLaterFirstPrefix[0]); 1074 int8_t earliestFirstLength = sizeof(gEarlierFirstPrefix)/sizeof(gEarlierFirstPrefix[0]); 1075 UnicodeString realPattern; 1076 if ( intervalPattern.startsWith(gLaterFirstPrefix, prefixLength) ) { 1077 order = true; 1078 intervalPattern.extract(prefixLength, 1079 intervalPattern.length() - prefixLength, 1080 realPattern); 1081 pattern = &realPattern; 1082 } else if ( intervalPattern.startsWith(gEarlierFirstPrefix, 1083 earliestFirstLength) ) { 1084 order = false; 1085 intervalPattern.extract(earliestFirstLength, 1086 intervalPattern.length() - earliestFirstLength, 1087 realPattern); 1088 pattern = &realPattern; 1089 } 1090 1091 int32_t splitPoint = splitPatternInto2Part(*pattern); 1092 1093 UnicodeString firstPart; 1094 UnicodeString secondPart; 1095 pattern->extract(0, splitPoint, firstPart); 1096 if ( splitPoint < pattern->length() ) { 1097 pattern->extract(splitPoint, pattern->length()-splitPoint, secondPart); 1098 } 1099 setPatternInfo(field, &firstPart, &secondPart, order); 1100 } 1101 1102 1103 1104 1105 /** 1106 * Generate interval pattern from existing resource 1107 * 1108 * It not only save the interval patterns, 1109 * but also return the extended skeleton and its best match skeleton. 1110 * 1111 * @param field largest different calendar field 1112 * @param skeleton skeleton 1113 * @param bestSkeleton the best match skeleton which has interval pattern 1114 * defined in resource 1115 * @param differenceInfo the difference between skeleton and best skeleton 1116 * 0 means the best matched skeleton is the same as input skeleton 1117 * 1 means the fields are the same, but field width are different 1118 * 2 means the only difference between fields are v/z, 1119 * -1 means there are other fields difference 1120 * 1121 * @param extendedSkeleton extended skeleton 1122 * @param extendedBestSkeleton extended best match skeleton 1123 * @return whether the interval pattern is found 1124 * through extending skeleton or not. 1125 * TRUE if interval pattern is found by 1126 * extending skeleton, FALSE otherwise. 1127 * @stable ICU 4.0 1128 */ 1129 UBool 1130 DateIntervalFormat::setIntervalPattern(UCalendarDateFields field, 1131 const UnicodeString* skeleton, 1132 const UnicodeString* bestSkeleton, 1133 int8_t differenceInfo, 1134 UnicodeString* extendedSkeleton, 1135 UnicodeString* extendedBestSkeleton) { 1136 UErrorCode status = U_ZERO_ERROR; 1137 // following getIntervalPattern() should not generate error status 1138 UnicodeString pattern; 1139 fInfo->getIntervalPattern(*bestSkeleton, field, pattern, status); 1140 if ( pattern.isEmpty() ) { 1141 // single date 1142 if ( SimpleDateFormat::isFieldUnitIgnored(*bestSkeleton, field) ) { 1143 // do nothing, format will handle it 1144 return false; 1145 } 1146 1147 // for 24 hour system, interval patterns in resource file 1148 // might not include pattern when am_pm differ, 1149 // which should be the same as hour differ. 1150 // add it here for simplicity 1151 if ( field == UCAL_AM_PM ) { 1152 fInfo->getIntervalPattern(*bestSkeleton, UCAL_HOUR, pattern,status); 1153 if ( !pattern.isEmpty() ) { 1154 setIntervalPattern(field, pattern); 1155 } 1156 return false; 1157 } 1158 // else, looking for pattern when 'y' differ for 'dMMMM' skeleton, 1159 // first, get best match pattern "MMMd", 1160 // since there is no pattern for 'y' differs for skeleton 'MMMd', 1161 // need to look for it from skeleton 'yMMMd', 1162 // if found, adjust field width in interval pattern from 1163 // "MMM" to "MMMM". 1164 UChar fieldLetter = fgCalendarFieldToPatternLetter[field]; 1165 if ( extendedSkeleton ) { 1166 *extendedSkeleton = *skeleton; 1167 *extendedBestSkeleton = *bestSkeleton; 1168 extendedSkeleton->insert(0, fieldLetter); 1169 extendedBestSkeleton->insert(0, fieldLetter); 1170 // for example, looking for patterns when 'y' differ for 1171 // skeleton "MMMM". 1172 fInfo->getIntervalPattern(*extendedBestSkeleton,field,pattern,status); 1173 if ( pattern.isEmpty() && differenceInfo == 0 ) { 1174 // if there is no skeleton "yMMMM" defined, 1175 // look for the best match skeleton, for example: "yMMM" 1176 const UnicodeString* tmpBest = fInfo->getBestSkeleton( 1177 *extendedBestSkeleton, differenceInfo); 1178 if ( tmpBest != 0 && differenceInfo != -1 ) { 1179 fInfo->getIntervalPattern(*tmpBest, field, pattern, status); 1180 bestSkeleton = tmpBest; 1181 } 1182 } 1183 } 1184 } 1185 if ( !pattern.isEmpty() ) { 1186 if ( differenceInfo != 0 ) { 1187 UnicodeString adjustIntervalPattern; 1188 adjustFieldWidth(*skeleton, *bestSkeleton, pattern, differenceInfo, 1189 adjustIntervalPattern); 1190 setIntervalPattern(field, adjustIntervalPattern); 1191 } else { 1192 setIntervalPattern(field, pattern); 1193 } 1194 if ( extendedSkeleton && !extendedSkeleton->isEmpty() ) { 1195 return TRUE; 1196 } 1197 } 1198 return FALSE; 1199 } 1200 1201 1202 1203 int32_t U_EXPORT2 1204 DateIntervalFormat::splitPatternInto2Part(const UnicodeString& intervalPattern) { 1205 UBool inQuote = false; 1206 UChar prevCh = 0; 1207 int32_t count = 0; 1208 1209 /* repeatedPattern used to record whether a pattern has already seen. 1210 It is a pattern applies to first calendar if it is first time seen, 1211 otherwise, it is a pattern applies to the second calendar 1212 */ 1213 UBool patternRepeated[] = 1214 { 1215 // A B C D E F G H I J K L M N O 1216 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1217 // P Q R S T U V W X Y Z 1218 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1219 // a b c d e f g h i j k l m n o 1220 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1221 // p q r s t u v w x y z 1222 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 1223 }; 1224 1225 int8_t PATTERN_CHAR_BASE = 0x41; 1226 1227 /* loop through the pattern string character by character looking for 1228 * the first repeated pattern letter, which breaks the interval pattern 1229 * into 2 parts. 1230 */ 1231 int32_t i; 1232 UBool foundRepetition = false; 1233 for (i = 0; i < intervalPattern.length(); ++i) { 1234 UChar ch = intervalPattern.charAt(i); 1235 1236 if (ch != prevCh && count > 0) { 1237 // check the repeativeness of pattern letter 1238 UBool repeated = patternRepeated[(int)(prevCh - PATTERN_CHAR_BASE)]; 1239 if ( repeated == FALSE ) { 1240 patternRepeated[prevCh - PATTERN_CHAR_BASE] = TRUE; 1241 } else { 1242 foundRepetition = true; 1243 break; 1244 } 1245 count = 0; 1246 } 1247 if (ch == '\'') { 1248 // Consecutive single quotes are a single quote literal, 1249 // either outside of quotes or between quotes 1250 if ((i+1) < intervalPattern.length() && 1251 intervalPattern.charAt(i+1) == '\'') { 1252 ++i; 1253 } else { 1254 inQuote = ! inQuote; 1255 } 1256 } 1257 else if (!inQuote && ((ch >= 0x0061 /*'a'*/ && ch <= 0x007A /*'z'*/) 1258 || (ch >= 0x0041 /*'A'*/ && ch <= 0x005A /*'Z'*/))) { 1259 // ch is a date-time pattern character 1260 prevCh = ch; 1261 ++count; 1262 } 1263 } 1264 // check last pattern char, distinguish 1265 // "dd MM" ( no repetition ), 1266 // "d-d"(last char repeated ), and 1267 // "d-d MM" ( repetition found ) 1268 if ( count > 0 && foundRepetition == FALSE ) { 1269 if ( patternRepeated[(int)(prevCh - PATTERN_CHAR_BASE)] == FALSE ) { 1270 count = 0; 1271 } 1272 } 1273 return (i - count); 1274 } 1275 1276 1277 1278 UnicodeString& 1279 DateIntervalFormat::fallbackFormat(Calendar& fromCalendar, 1280 Calendar& toCalendar, 1281 UnicodeString& appendTo, 1282 FieldPosition& pos, 1283 UErrorCode& status) const { 1284 if ( U_FAILURE(status) ) { 1285 return appendTo; 1286 } 1287 // the fall back 1288 // no need delete earlierDate and laterDate since they are adopted 1289 UnicodeString* earlierDate = new UnicodeString(); 1290 *earlierDate = fDateFormat->format(fromCalendar, *earlierDate, pos); 1291 UnicodeString* laterDate = new UnicodeString(); 1292 *laterDate = fDateFormat->format(toCalendar, *laterDate, pos); 1293 UnicodeString fallbackPattern; 1294 fInfo->getFallbackIntervalPattern(fallbackPattern); 1295 Formattable fmtArray[2]; 1296 fmtArray[0].adoptString(earlierDate); 1297 fmtArray[1].adoptString(laterDate); 1298 1299 UnicodeString fallback; 1300 MessageFormat::format(fallbackPattern, fmtArray, 2, fallback, status); 1301 if ( U_SUCCESS(status) ) { 1302 appendTo.append(fallback); 1303 } 1304 return appendTo; 1305 } 1306 1307 1308 1309 1310 UBool U_EXPORT2 1311 DateIntervalFormat::fieldExistsInSkeleton(UCalendarDateFields field, 1312 const UnicodeString& skeleton) 1313 { 1314 const UChar fieldChar = fgCalendarFieldToPatternLetter[field]; 1315 return ( (skeleton.indexOf(fieldChar) == -1)?FALSE:TRUE ) ; 1316 } 1317 1318 1319 1320 void U_EXPORT2 1321 DateIntervalFormat::adjustFieldWidth(const UnicodeString& inputSkeleton, 1322 const UnicodeString& bestMatchSkeleton, 1323 const UnicodeString& bestIntervalPattern, 1324 int8_t differenceInfo, 1325 UnicodeString& adjustedPtn) { 1326 adjustedPtn = bestIntervalPattern; 1327 int32_t inputSkeletonFieldWidth[] = 1328 { 1329 // A B C D E F G H I J K L M N O 1330 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1331 // P Q R S T U V W X Y Z 1332 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1333 // a b c d e f g h i j k l m n o 1334 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1335 // p q r s t u v w x y z 1336 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 1337 }; 1338 1339 int32_t bestMatchSkeletonFieldWidth[] = 1340 { 1341 // A B C D E F G H I J K L M N O 1342 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1343 // P Q R S T U V W X Y Z 1344 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1345 // a b c d e f g h i j k l m n o 1346 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1347 // p q r s t u v w x y z 1348 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 1349 }; 1350 1351 DateIntervalInfo::parseSkeleton(inputSkeleton, inputSkeletonFieldWidth); 1352 DateIntervalInfo::parseSkeleton(bestMatchSkeleton, bestMatchSkeletonFieldWidth); 1353 if ( differenceInfo == 2 ) { 1354 adjustedPtn.findAndReplace(UnicodeString((UChar)0x76 /* v */), 1355 UnicodeString((UChar)0x7a /* z */)); 1356 } 1357 1358 UBool inQuote = false; 1359 UChar prevCh = 0; 1360 int32_t count = 0; 1361 1362 const int8_t PATTERN_CHAR_BASE = 0x41; 1363 1364 // loop through the pattern string character by character 1365 int32_t adjustedPtnLength = adjustedPtn.length(); 1366 int32_t i; 1367 for (i = 0; i < adjustedPtnLength; ++i) { 1368 UChar ch = adjustedPtn.charAt(i); 1369 if (ch != prevCh && count > 0) { 1370 // check the repeativeness of pattern letter 1371 UChar skeletonChar = prevCh; 1372 if ( skeletonChar == CAP_L ) { 1373 // there is no "L" (always be "M") in skeleton, 1374 // but there is "L" in pattern. 1375 // for skeleton "M+", the pattern might be "...L..." 1376 skeletonChar = CAP_M; 1377 } 1378 int32_t fieldCount = bestMatchSkeletonFieldWidth[(int)(skeletonChar - PATTERN_CHAR_BASE)]; 1379 int32_t inputFieldCount = inputSkeletonFieldWidth[(int)(skeletonChar - PATTERN_CHAR_BASE)]; 1380 if ( fieldCount == count && inputFieldCount > fieldCount ) { 1381 count = inputFieldCount - fieldCount; 1382 int32_t j; 1383 for ( j = 0; j < count; ++j ) { 1384 adjustedPtn.insert(i, prevCh); 1385 } 1386 i += count; 1387 adjustedPtnLength += count; 1388 } 1389 count = 0; 1390 } 1391 if (ch == '\'') { 1392 // Consecutive single quotes are a single quote literal, 1393 // either outside of quotes or between quotes 1394 if ((i+1) < adjustedPtn.length() && adjustedPtn.charAt(i+1) == '\'') { 1395 ++i; 1396 } else { 1397 inQuote = ! inQuote; 1398 } 1399 } 1400 else if ( ! inQuote && ((ch >= 0x0061 /*'a'*/ && ch <= 0x007A /*'z'*/) 1401 || (ch >= 0x0041 /*'A'*/ && ch <= 0x005A /*'Z'*/))) { 1402 // ch is a date-time pattern character 1403 prevCh = ch; 1404 ++count; 1405 } 1406 } 1407 if ( count > 0 ) { 1408 // last item 1409 // check the repeativeness of pattern letter 1410 UChar skeletonChar = prevCh; 1411 if ( skeletonChar == CAP_L ) { 1412 // there is no "L" (always be "M") in skeleton, 1413 // but there is "L" in pattern. 1414 // for skeleton "M+", the pattern might be "...L..." 1415 skeletonChar = CAP_M; 1416 } 1417 int32_t fieldCount = bestMatchSkeletonFieldWidth[(int)(skeletonChar - PATTERN_CHAR_BASE)]; 1418 int32_t inputFieldCount = inputSkeletonFieldWidth[(int)(skeletonChar - PATTERN_CHAR_BASE)]; 1419 if ( fieldCount == count && inputFieldCount > fieldCount ) { 1420 count = inputFieldCount - fieldCount; 1421 int32_t j; 1422 for ( j = 0; j < count; ++j ) { 1423 adjustedPtn.append(prevCh); 1424 } 1425 } 1426 } 1427 } 1428 1429 1430 1431 void 1432 DateIntervalFormat::concatSingleDate2TimeInterval(const UChar* format, 1433 int32_t formatLen, 1434 const UnicodeString& datePattern, 1435 UCalendarDateFields field, 1436 UErrorCode& status) { 1437 // following should not set wrong status 1438 int32_t itvPtnIndex = DateIntervalInfo::calendarFieldToIntervalIndex(field, 1439 status); 1440 if ( U_FAILURE(status) ) { 1441 return; 1442 } 1443 PatternInfo& timeItvPtnInfo = fIntervalPatterns[itvPtnIndex]; 1444 if ( !timeItvPtnInfo.firstPart.isEmpty() ) { 1445 // UnicodeString allocated here is adopted, so no need to delete 1446 UnicodeString* timeIntervalPattern = new UnicodeString(timeItvPtnInfo.firstPart); 1447 timeIntervalPattern->append(timeItvPtnInfo.secondPart); 1448 UnicodeString* dateStr = new UnicodeString(datePattern); 1449 Formattable fmtArray[2]; 1450 fmtArray[0].adoptString(timeIntervalPattern); 1451 fmtArray[1].adoptString(dateStr); 1452 UnicodeString combinedPattern; 1453 MessageFormat::format(UnicodeString(TRUE, format, formatLen), 1454 fmtArray, 2, combinedPattern, status); 1455 if ( U_FAILURE(status) ) { 1456 return; 1457 } 1458 setIntervalPattern(field, combinedPattern, timeItvPtnInfo.laterDateFirst); 1459 } 1460 // else: fall back 1461 // it should not happen if the interval format defined is valid 1462 } 1463 1464 1465 1466 const UChar 1467 DateIntervalFormat::fgCalendarFieldToPatternLetter[] = 1468 { 1469 /*GyM*/ CAP_G, LOW_Y, CAP_M, 1470 /*wWd*/ LOW_W, CAP_W, LOW_D, 1471 /*DEF*/ CAP_D, CAP_E, CAP_F, 1472 /*ahH*/ LOW_A, LOW_H, CAP_H, 1473 /*m..*/ LOW_M, 1474 }; 1475 1476 1477 U_NAMESPACE_END 1478 1479 #endif 1480