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