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