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