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