1/******************************************************************************* 2* Copyright (C) 2008-2012, 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 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 533DateIntervalFormat* U_EXPORT2 534DateIntervalFormat::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 */ 587void 588DateIntervalFormat::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 771void U_EXPORT2 772DateIntervalFormat::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 normalizedDateSkeleton.append(LOW_Y); 867 } 868 if ( MCount != 0 ) { 869 if ( MCount < 3 ) { 870 normalizedDateSkeleton.append(CAP_M); 871 } else { 872 int32_t i; 873 for ( i = 0; i < MCount && i < MAX_M_COUNT; ++i ) { 874 normalizedDateSkeleton.append(CAP_M); 875 } 876 } 877 } 878 if ( ECount != 0 ) { 879 if ( ECount <= 3 ) { 880 normalizedDateSkeleton.append(CAP_E); 881 } else { 882 int32_t i; 883 for ( i = 0; i < ECount && i < MAX_E_COUNT; ++i ) { 884 normalizedDateSkeleton.append(CAP_E); 885 } 886 } 887 } 888 if ( dCount != 0 ) { 889 normalizedDateSkeleton.append(LOW_D); 890 } 891 892 /* generate normalized form for time */ 893 if ( HCount != 0 ) { 894 normalizedTimeSkeleton.append(CAP_H); 895 } 896 else if ( hCount != 0 ) { 897 normalizedTimeSkeleton.append(LOW_H); 898 } 899 if ( mCount != 0 ) { 900 normalizedTimeSkeleton.append(LOW_M); 901 } 902 if ( zCount != 0 ) { 903 normalizedTimeSkeleton.append(LOW_Z); 904 } 905 if ( vCount != 0 ) { 906 normalizedTimeSkeleton.append(LOW_V); 907 } 908} 909 910 911/** 912 * Generate date or time interval pattern from resource, 913 * and set them into the interval pattern locale to this formatter. 914 * 915 * It needs to handle the following: 916 * 1. need to adjust field width. 917 * For example, the interval patterns saved in DateIntervalInfo 918 * includes "dMMMy", but not "dMMMMy". 919 * Need to get interval patterns for dMMMMy from dMMMy. 920 * Another example, the interval patterns saved in DateIntervalInfo 921 * includes "hmv", but not "hmz". 922 * Need to get interval patterns for "hmz' from 'hmv' 923 * 924 * 2. there might be no pattern for 'y' differ for skeleton "Md", 925 * in order to get interval patterns for 'y' differ, 926 * need to look for it from skeleton 'yMd' 927 * 928 * @param dateSkeleton normalized date skeleton 929 * @param timeSkeleton normalized time skeleton 930 * @return whether the resource is found for the skeleton. 931 * TRUE if interval pattern found for the skeleton, 932 * FALSE otherwise. 933 * @stable ICU 4.0 934 */ 935UBool 936DateIntervalFormat::setSeparateDateTimePtn( 937 const UnicodeString& dateSkeleton, 938 const UnicodeString& timeSkeleton) { 939 const UnicodeString* skeleton; 940 // if both date and time skeleton present, 941 // the final interval pattern might include time interval patterns 942 // ( when, am_pm, hour, minute differ ), 943 // but not date interval patterns ( when year, month, day differ ). 944 // For year/month/day differ, it falls back to fall-back pattern. 945 if ( timeSkeleton.length() != 0 ) { 946 skeleton = &timeSkeleton; 947 } else { 948 skeleton = &dateSkeleton; 949 } 950 951 /* interval patterns for skeleton "dMMMy" (but not "dMMMMy") 952 * are defined in resource, 953 * interval patterns for skeleton "dMMMMy" are calculated by 954 * 1. get the best match skeleton for "dMMMMy", which is "dMMMy" 955 * 2. get the interval patterns for "dMMMy", 956 * 3. extend "MMM" to "MMMM" in above interval patterns for "dMMMMy" 957 * getBestSkeleton() is step 1. 958 */ 959 // best skeleton, and the difference information 960 int8_t differenceInfo = 0; 961 const UnicodeString* bestSkeleton = fInfo->getBestSkeleton(*skeleton, 962 differenceInfo); 963 /* best skeleton could be NULL. 964 For example: in "ca" resource file, 965 interval format is defined as following 966 intervalFormats{ 967 fallback{"{0} - {1}"} 968 } 969 there is no skeletons/interval patterns defined, 970 and the best skeleton match could be NULL 971 */ 972 if ( bestSkeleton == NULL ) { 973 return false; 974 } 975 976 // difference: 977 // 0 means the best matched skeleton is the same as input skeleton 978 // 1 means the fields are the same, but field width are different 979 // 2 means the only difference between fields are v/z, 980 // -1 means there are other fields difference 981 if ( differenceInfo == -1 ) { 982 // skeleton has different fields, not only v/z difference 983 return false; 984 } 985 986 if ( timeSkeleton.length() == 0 ) { 987 UnicodeString extendedSkeleton; 988 UnicodeString extendedBestSkeleton; 989 // only has date skeleton 990 setIntervalPattern(UCAL_DATE, skeleton, bestSkeleton, differenceInfo, 991 &extendedSkeleton, &extendedBestSkeleton); 992 993 UBool extended = setIntervalPattern(UCAL_MONTH, skeleton, bestSkeleton, 994 differenceInfo, 995 &extendedSkeleton, &extendedBestSkeleton); 996 997 if ( extended ) { 998 bestSkeleton = &extendedBestSkeleton; 999 skeleton = &extendedSkeleton; 1000 } 1001 setIntervalPattern(UCAL_YEAR, skeleton, bestSkeleton, differenceInfo, 1002 &extendedSkeleton, &extendedBestSkeleton); 1003 } else { 1004 setIntervalPattern(UCAL_MINUTE, skeleton, bestSkeleton, differenceInfo); 1005 setIntervalPattern(UCAL_HOUR, skeleton, bestSkeleton, differenceInfo); 1006 setIntervalPattern(UCAL_AM_PM, skeleton, bestSkeleton, differenceInfo); 1007 } 1008 return true; 1009} 1010 1011 1012 1013void 1014DateIntervalFormat::setFallbackPattern(UCalendarDateFields field, 1015 const UnicodeString& skeleton, 1016 UErrorCode& status) { 1017 if ( U_FAILURE(status) ) { 1018 return; 1019 } 1020 UnicodeString pattern = fDtpng->getBestPattern(skeleton, status); 1021 if ( U_FAILURE(status) ) { 1022 return; 1023 } 1024 setPatternInfo(field, NULL, &pattern, fInfo->getDefaultOrder()); 1025} 1026 1027 1028 1029 1030void 1031DateIntervalFormat::setPatternInfo(UCalendarDateFields field, 1032 const UnicodeString* firstPart, 1033 const UnicodeString* secondPart, 1034 UBool laterDateFirst) { 1035 // for fall back interval patterns, 1036 // the first part of the pattern is empty, 1037 // the second part of the pattern is the full-pattern 1038 // should be used in fall-back. 1039 UErrorCode status = U_ZERO_ERROR; 1040 // following should not set any wrong status. 1041 int32_t itvPtnIndex = DateIntervalInfo::calendarFieldToIntervalIndex(field, 1042 status); 1043 if ( U_FAILURE(status) ) { 1044 return; 1045 } 1046 PatternInfo& ptn = fIntervalPatterns[itvPtnIndex]; 1047 if ( firstPart ) { 1048 ptn.firstPart = *firstPart; 1049 } 1050 if ( secondPart ) { 1051 ptn.secondPart = *secondPart; 1052 } 1053 ptn.laterDateFirst = laterDateFirst; 1054} 1055 1056void 1057DateIntervalFormat::setIntervalPattern(UCalendarDateFields field, 1058 const UnicodeString& intervalPattern) { 1059 UBool order = fInfo->getDefaultOrder(); 1060 setIntervalPattern(field, intervalPattern, order); 1061} 1062 1063 1064void 1065DateIntervalFormat::setIntervalPattern(UCalendarDateFields field, 1066 const UnicodeString& intervalPattern, 1067 UBool laterDateFirst) { 1068 const UnicodeString* pattern = &intervalPattern; 1069 UBool order = laterDateFirst; 1070 // check for "latestFirst:" or "earliestFirst:" prefix 1071 int8_t prefixLength = sizeof(gLaterFirstPrefix)/sizeof(gLaterFirstPrefix[0]); 1072 int8_t earliestFirstLength = sizeof(gEarlierFirstPrefix)/sizeof(gEarlierFirstPrefix[0]); 1073 UnicodeString realPattern; 1074 if ( intervalPattern.startsWith(gLaterFirstPrefix, prefixLength) ) { 1075 order = true; 1076 intervalPattern.extract(prefixLength, 1077 intervalPattern.length() - prefixLength, 1078 realPattern); 1079 pattern = &realPattern; 1080 } else if ( intervalPattern.startsWith(gEarlierFirstPrefix, 1081 earliestFirstLength) ) { 1082 order = false; 1083 intervalPattern.extract(earliestFirstLength, 1084 intervalPattern.length() - earliestFirstLength, 1085 realPattern); 1086 pattern = &realPattern; 1087 } 1088 1089 int32_t splitPoint = splitPatternInto2Part(*pattern); 1090 1091 UnicodeString firstPart; 1092 UnicodeString secondPart; 1093 pattern->extract(0, splitPoint, firstPart); 1094 if ( splitPoint < pattern->length() ) { 1095 pattern->extract(splitPoint, pattern->length()-splitPoint, secondPart); 1096 } 1097 setPatternInfo(field, &firstPart, &secondPart, order); 1098} 1099 1100 1101 1102 1103/** 1104 * Generate interval pattern from existing resource 1105 * 1106 * It not only save the interval patterns, 1107 * but also return the extended skeleton and its best match skeleton. 1108 * 1109 * @param field largest different calendar field 1110 * @param skeleton skeleton 1111 * @param bestSkeleton the best match skeleton which has interval pattern 1112 * defined in resource 1113 * @param differenceInfo the difference between skeleton and best skeleton 1114 * 0 means the best matched skeleton is the same as input skeleton 1115 * 1 means the fields are the same, but field width are different 1116 * 2 means the only difference between fields are v/z, 1117 * -1 means there are other fields difference 1118 * 1119 * @param extendedSkeleton extended skeleton 1120 * @param extendedBestSkeleton extended best match skeleton 1121 * @return whether the interval pattern is found 1122 * through extending skeleton or not. 1123 * TRUE if interval pattern is found by 1124 * extending skeleton, FALSE otherwise. 1125 * @stable ICU 4.0 1126 */ 1127UBool 1128DateIntervalFormat::setIntervalPattern(UCalendarDateFields field, 1129 const UnicodeString* skeleton, 1130 const UnicodeString* bestSkeleton, 1131 int8_t differenceInfo, 1132 UnicodeString* extendedSkeleton, 1133 UnicodeString* extendedBestSkeleton) { 1134 UErrorCode status = U_ZERO_ERROR; 1135 // following getIntervalPattern() should not generate error status 1136 UnicodeString pattern; 1137 fInfo->getIntervalPattern(*bestSkeleton, field, pattern, status); 1138 if ( pattern.isEmpty() ) { 1139 // single date 1140 if ( SimpleDateFormat::isFieldUnitIgnored(*bestSkeleton, field) ) { 1141 // do nothing, format will handle it 1142 return false; 1143 } 1144 1145 // for 24 hour system, interval patterns in resource file 1146 // might not include pattern when am_pm differ, 1147 // which should be the same as hour differ. 1148 // add it here for simplicity 1149 if ( field == UCAL_AM_PM ) { 1150 fInfo->getIntervalPattern(*bestSkeleton, UCAL_HOUR, pattern,status); 1151 if ( !pattern.isEmpty() ) { 1152 setIntervalPattern(field, pattern); 1153 } 1154 return false; 1155 } 1156 // else, looking for pattern when 'y' differ for 'dMMMM' skeleton, 1157 // first, get best match pattern "MMMd", 1158 // since there is no pattern for 'y' differs for skeleton 'MMMd', 1159 // need to look for it from skeleton 'yMMMd', 1160 // if found, adjust field width in interval pattern from 1161 // "MMM" to "MMMM". 1162 UChar fieldLetter = fgCalendarFieldToPatternLetter[field]; 1163 if ( extendedSkeleton ) { 1164 *extendedSkeleton = *skeleton; 1165 *extendedBestSkeleton = *bestSkeleton; 1166 extendedSkeleton->insert(0, fieldLetter); 1167 extendedBestSkeleton->insert(0, fieldLetter); 1168 // for example, looking for patterns when 'y' differ for 1169 // skeleton "MMMM". 1170 fInfo->getIntervalPattern(*extendedBestSkeleton,field,pattern,status); 1171 if ( pattern.isEmpty() && differenceInfo == 0 ) { 1172 // if there is no skeleton "yMMMM" defined, 1173 // look for the best match skeleton, for example: "yMMM" 1174 const UnicodeString* tmpBest = fInfo->getBestSkeleton( 1175 *extendedBestSkeleton, differenceInfo); 1176 if ( tmpBest != 0 && differenceInfo != -1 ) { 1177 fInfo->getIntervalPattern(*tmpBest, field, pattern, status); 1178 bestSkeleton = tmpBest; 1179 } 1180 } 1181 } 1182 } 1183 if ( !pattern.isEmpty() ) { 1184 if ( differenceInfo != 0 ) { 1185 UnicodeString adjustIntervalPattern; 1186 adjustFieldWidth(*skeleton, *bestSkeleton, pattern, differenceInfo, 1187 adjustIntervalPattern); 1188 setIntervalPattern(field, adjustIntervalPattern); 1189 } else { 1190 setIntervalPattern(field, pattern); 1191 } 1192 if ( extendedSkeleton && !extendedSkeleton->isEmpty() ) { 1193 return TRUE; 1194 } 1195 } 1196 return FALSE; 1197} 1198 1199 1200 1201int32_t U_EXPORT2 1202DateIntervalFormat::splitPatternInto2Part(const UnicodeString& intervalPattern) { 1203 UBool inQuote = false; 1204 UChar prevCh = 0; 1205 int32_t count = 0; 1206 1207 /* repeatedPattern used to record whether a pattern has already seen. 1208 It is a pattern applies to first calendar if it is first time seen, 1209 otherwise, it is a pattern applies to the second calendar 1210 */ 1211 UBool patternRepeated[] = 1212 { 1213 // A B C D E F G H I J K L M N O 1214 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1215 // P Q R S T U V W X Y Z 1216 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1217 // a b c d e f g h i j k l m n o 1218 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1219 // p q r s t u v w x y z 1220 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 1221 }; 1222 1223 int8_t PATTERN_CHAR_BASE = 0x41; 1224 1225 /* loop through the pattern string character by character looking for 1226 * the first repeated pattern letter, which breaks the interval pattern 1227 * into 2 parts. 1228 */ 1229 int32_t i; 1230 UBool foundRepetition = false; 1231 for (i = 0; i < intervalPattern.length(); ++i) { 1232 UChar ch = intervalPattern.charAt(i); 1233 1234 if (ch != prevCh && count > 0) { 1235 // check the repeativeness of pattern letter 1236 UBool repeated = patternRepeated[(int)(prevCh - PATTERN_CHAR_BASE)]; 1237 if ( repeated == FALSE ) { 1238 patternRepeated[prevCh - PATTERN_CHAR_BASE] = TRUE; 1239 } else { 1240 foundRepetition = true; 1241 break; 1242 } 1243 count = 0; 1244 } 1245 if (ch == '\'') { 1246 // Consecutive single quotes are a single quote literal, 1247 // either outside of quotes or between quotes 1248 if ((i+1) < intervalPattern.length() && 1249 intervalPattern.charAt(i+1) == '\'') { 1250 ++i; 1251 } else { 1252 inQuote = ! inQuote; 1253 } 1254 } 1255 else if (!inQuote && ((ch >= 0x0061 /*'a'*/ && ch <= 0x007A /*'z'*/) 1256 || (ch >= 0x0041 /*'A'*/ && ch <= 0x005A /*'Z'*/))) { 1257 // ch is a date-time pattern character 1258 prevCh = ch; 1259 ++count; 1260 } 1261 } 1262 // check last pattern char, distinguish 1263 // "dd MM" ( no repetition ), 1264 // "d-d"(last char repeated ), and 1265 // "d-d MM" ( repetition found ) 1266 if ( count > 0 && foundRepetition == FALSE ) { 1267 if ( patternRepeated[(int)(prevCh - PATTERN_CHAR_BASE)] == FALSE ) { 1268 count = 0; 1269 } 1270 } 1271 return (i - count); 1272} 1273 1274 1275 1276UnicodeString& 1277DateIntervalFormat::fallbackFormat(Calendar& fromCalendar, 1278 Calendar& toCalendar, 1279 UnicodeString& appendTo, 1280 FieldPosition& pos, 1281 UErrorCode& status) const { 1282 if ( U_FAILURE(status) ) { 1283 return appendTo; 1284 } 1285 // the fall back 1286 // no need delete earlierDate and laterDate since they are adopted 1287 UnicodeString* earlierDate = new UnicodeString(); 1288 *earlierDate = fDateFormat->format(fromCalendar, *earlierDate, pos); 1289 UnicodeString* laterDate = new UnicodeString(); 1290 *laterDate = fDateFormat->format(toCalendar, *laterDate, pos); 1291 UnicodeString fallbackPattern; 1292 fInfo->getFallbackIntervalPattern(fallbackPattern); 1293 Formattable fmtArray[2]; 1294 fmtArray[0].adoptString(earlierDate); 1295 fmtArray[1].adoptString(laterDate); 1296 1297 UnicodeString fallback; 1298 MessageFormat::format(fallbackPattern, fmtArray, 2, fallback, status); 1299 if ( U_SUCCESS(status) ) { 1300 appendTo.append(fallback); 1301 } 1302 return appendTo; 1303} 1304 1305 1306 1307 1308UBool U_EXPORT2 1309DateIntervalFormat::fieldExistsInSkeleton(UCalendarDateFields field, 1310 const UnicodeString& skeleton) 1311{ 1312 const UChar fieldChar = fgCalendarFieldToPatternLetter[field]; 1313 return ( (skeleton.indexOf(fieldChar) == -1)?FALSE:TRUE ) ; 1314} 1315 1316 1317 1318void U_EXPORT2 1319DateIntervalFormat::adjustFieldWidth(const UnicodeString& inputSkeleton, 1320 const UnicodeString& bestMatchSkeleton, 1321 const UnicodeString& bestIntervalPattern, 1322 int8_t differenceInfo, 1323 UnicodeString& adjustedPtn) { 1324 adjustedPtn = bestIntervalPattern; 1325 int32_t inputSkeletonFieldWidth[] = 1326 { 1327 // A B C D E F G H I J K L M N O 1328 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1329 // P Q R S T U V W X Y Z 1330 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1331 // a b c d e f g h i j k l m n o 1332 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1333 // p q r s t u v w x y z 1334 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 1335 }; 1336 1337 int32_t bestMatchSkeletonFieldWidth[] = 1338 { 1339 // A B C D E F G H I J K L M N O 1340 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1341 // P Q R S T U V W X Y Z 1342 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1343 // a b c d e f g h i j k l m n o 1344 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1345 // p q r s t u v w x y z 1346 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 1347 }; 1348 1349 DateIntervalInfo::parseSkeleton(inputSkeleton, inputSkeletonFieldWidth); 1350 DateIntervalInfo::parseSkeleton(bestMatchSkeleton, bestMatchSkeletonFieldWidth); 1351 if ( differenceInfo == 2 ) { 1352 adjustedPtn.findAndReplace(UnicodeString((UChar)0x76 /* v */), 1353 UnicodeString((UChar)0x7a /* z */)); 1354 } 1355 1356 UBool inQuote = false; 1357 UChar prevCh = 0; 1358 int32_t count = 0; 1359 1360 const int8_t PATTERN_CHAR_BASE = 0x41; 1361 1362 // loop through the pattern string character by character 1363 int32_t adjustedPtnLength = adjustedPtn.length(); 1364 int32_t i; 1365 for (i = 0; i < adjustedPtnLength; ++i) { 1366 UChar ch = adjustedPtn.charAt(i); 1367 if (ch != prevCh && count > 0) { 1368 // check the repeativeness of pattern letter 1369 UChar skeletonChar = prevCh; 1370 if ( skeletonChar == CAP_L ) { 1371 // there is no "L" (always be "M") in skeleton, 1372 // but there is "L" in pattern. 1373 // for skeleton "M+", the pattern might be "...L..." 1374 skeletonChar = CAP_M; 1375 } 1376 int32_t fieldCount = bestMatchSkeletonFieldWidth[(int)(skeletonChar - PATTERN_CHAR_BASE)]; 1377 int32_t inputFieldCount = inputSkeletonFieldWidth[(int)(skeletonChar - PATTERN_CHAR_BASE)]; 1378 if ( fieldCount == count && inputFieldCount > fieldCount ) { 1379 count = inputFieldCount - fieldCount; 1380 int32_t j; 1381 for ( j = 0; j < count; ++j ) { 1382 adjustedPtn.insert(i, prevCh); 1383 } 1384 i += count; 1385 adjustedPtnLength += count; 1386 } 1387 count = 0; 1388 } 1389 if (ch == '\'') { 1390 // Consecutive single quotes are a single quote literal, 1391 // either outside of quotes or between quotes 1392 if ((i+1) < adjustedPtn.length() && adjustedPtn.charAt(i+1) == '\'') { 1393 ++i; 1394 } else { 1395 inQuote = ! inQuote; 1396 } 1397 } 1398 else if ( ! inQuote && ((ch >= 0x0061 /*'a'*/ && ch <= 0x007A /*'z'*/) 1399 || (ch >= 0x0041 /*'A'*/ && ch <= 0x005A /*'Z'*/))) { 1400 // ch is a date-time pattern character 1401 prevCh = ch; 1402 ++count; 1403 } 1404 } 1405 if ( count > 0 ) { 1406 // last item 1407 // check the repeativeness of pattern letter 1408 UChar skeletonChar = prevCh; 1409 if ( skeletonChar == CAP_L ) { 1410 // there is no "L" (always be "M") in skeleton, 1411 // but there is "L" in pattern. 1412 // for skeleton "M+", the pattern might be "...L..." 1413 skeletonChar = CAP_M; 1414 } 1415 int32_t fieldCount = bestMatchSkeletonFieldWidth[(int)(skeletonChar - PATTERN_CHAR_BASE)]; 1416 int32_t inputFieldCount = inputSkeletonFieldWidth[(int)(skeletonChar - PATTERN_CHAR_BASE)]; 1417 if ( fieldCount == count && inputFieldCount > fieldCount ) { 1418 count = inputFieldCount - fieldCount; 1419 int32_t j; 1420 for ( j = 0; j < count; ++j ) { 1421 adjustedPtn.append(prevCh); 1422 } 1423 } 1424 } 1425} 1426 1427 1428 1429void 1430DateIntervalFormat::concatSingleDate2TimeInterval(const UChar* format, 1431 int32_t formatLen, 1432 const UnicodeString& datePattern, 1433 UCalendarDateFields field, 1434 UErrorCode& status) { 1435 // following should not set wrong status 1436 int32_t itvPtnIndex = DateIntervalInfo::calendarFieldToIntervalIndex(field, 1437 status); 1438 if ( U_FAILURE(status) ) { 1439 return; 1440 } 1441 PatternInfo& timeItvPtnInfo = fIntervalPatterns[itvPtnIndex]; 1442 if ( !timeItvPtnInfo.firstPart.isEmpty() ) { 1443 // UnicodeString allocated here is adopted, so no need to delete 1444 UnicodeString* timeIntervalPattern = new UnicodeString(timeItvPtnInfo.firstPart); 1445 timeIntervalPattern->append(timeItvPtnInfo.secondPart); 1446 UnicodeString* dateStr = new UnicodeString(datePattern); 1447 Formattable fmtArray[2]; 1448 fmtArray[0].adoptString(timeIntervalPattern); 1449 fmtArray[1].adoptString(dateStr); 1450 UnicodeString combinedPattern; 1451 MessageFormat::format(UnicodeString(TRUE, format, formatLen), 1452 fmtArray, 2, combinedPattern, status); 1453 if ( U_FAILURE(status) ) { 1454 return; 1455 } 1456 setIntervalPattern(field, combinedPattern, timeItvPtnInfo.laterDateFirst); 1457 } 1458 // else: fall back 1459 // it should not happen if the interval format defined is valid 1460} 1461 1462 1463 1464const UChar 1465DateIntervalFormat::fgCalendarFieldToPatternLetter[] = 1466{ 1467 /*GyM*/ CAP_G, LOW_Y, CAP_M, 1468 /*wWd*/ LOW_W, CAP_W, LOW_D, 1469 /*DEF*/ CAP_D, CAP_E, CAP_F, 1470 /*ahH*/ LOW_A, LOW_H, CAP_H, 1471 /*m..*/ LOW_M, 1472}; 1473 1474 1475U_NAMESPACE_END 1476 1477#endif 1478