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