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