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