1/* 2 ******************************************************************************* 3 * Copyright (C) 1997-2015, International Business Machines Corporation and * 4 * others. All Rights Reserved. * 5 ******************************************************************************* 6 * 7 * File DATEFMT.CPP 8 * 9 * Modification History: 10 * 11 * Date Name Description 12 * 02/19/97 aliu Converted from java. 13 * 03/31/97 aliu Modified extensively to work with 50 locales. 14 * 04/01/97 aliu Added support for centuries. 15 * 08/12/97 aliu Fixed operator== to use Calendar::equivalentTo. 16 * 07/20/98 stephen Changed ParsePosition initialization 17 ******************************************************************************** 18 */ 19 20#include "unicode/utypes.h" 21 22#if !UCONFIG_NO_FORMATTING 23 24#include "unicode/ures.h" 25#include "unicode/datefmt.h" 26#include "unicode/smpdtfmt.h" 27#include "unicode/dtptngen.h" 28#include "unicode/udisplaycontext.h" 29#include "reldtfmt.h" 30#include "sharedobject.h" 31#include "unifiedcache.h" 32#include "uarrsort.h" 33 34#include "cstring.h" 35#include "windtfmt.h" 36 37#if defined( U_DEBUG_CALSVC ) || defined (U_DEBUG_CAL) 38#include <stdio.h> 39#endif 40 41// ***************************************************************************** 42// class DateFormat 43// ***************************************************************************** 44 45U_NAMESPACE_BEGIN 46 47class U_I18N_API DateFmtBestPattern : public SharedObject { 48public: 49 UnicodeString fPattern; 50 51 DateFmtBestPattern(const UnicodeString &pattern) 52 : fPattern(pattern) { } 53 ~DateFmtBestPattern(); 54}; 55 56DateFmtBestPattern::~DateFmtBestPattern() { 57} 58 59template<> U_I18N_API 60const DateFmtBestPattern *LocaleCacheKey<DateFmtBestPattern>::createObject( 61 const void * /*creationContext*/, UErrorCode &status) const { 62 status = U_UNSUPPORTED_ERROR; 63 return NULL; 64} 65 66class U_I18N_API DateFmtBestPatternKey : public LocaleCacheKey<DateFmtBestPattern> { 67private: 68 UnicodeString fSkeleton; 69public: 70 DateFmtBestPatternKey( 71 const Locale &loc, 72 const UnicodeString &skeleton, 73 UErrorCode &status) 74 : LocaleCacheKey<DateFmtBestPattern>(loc), 75 fSkeleton(DateTimePatternGenerator::staticGetSkeleton(skeleton, status)) { } 76 DateFmtBestPatternKey(const DateFmtBestPatternKey &other) : 77 LocaleCacheKey<DateFmtBestPattern>(other), 78 fSkeleton(other.fSkeleton) { } 79 virtual ~DateFmtBestPatternKey(); 80 virtual int32_t hashCode() const { 81 return 37 * LocaleCacheKey<DateFmtBestPattern>::hashCode() + fSkeleton.hashCode(); 82 } 83 virtual UBool operator==(const CacheKeyBase &other) const { 84 // reflexive 85 if (this == &other) { 86 return TRUE; 87 } 88 if (!LocaleCacheKey<DateFmtBestPattern>::operator==(other)) { 89 return FALSE; 90 } 91 // We know that this and other are of same class if we get this far. 92 const DateFmtBestPatternKey &realOther = 93 static_cast<const DateFmtBestPatternKey &>(other); 94 return (realOther.fSkeleton == fSkeleton); 95 } 96 virtual CacheKeyBase *clone() const { 97 return new DateFmtBestPatternKey(*this); 98 } 99 virtual const DateFmtBestPattern *createObject( 100 const void * /*unused*/, UErrorCode &status) const { 101 LocalPointer<DateTimePatternGenerator> dtpg( 102 DateTimePatternGenerator::createInstance(fLoc, status)); 103 if (U_FAILURE(status)) { 104 return NULL; 105 } 106 107 LocalPointer<DateFmtBestPattern> pattern( 108 new DateFmtBestPattern( 109 dtpg->getBestPattern(fSkeleton, status)), 110 status); 111 if (U_FAILURE(status)) { 112 return NULL; 113 } 114 DateFmtBestPattern *result = pattern.orphan(); 115 result->addRef(); 116 return result; 117 } 118}; 119 120DateFmtBestPatternKey::~DateFmtBestPatternKey() { } 121 122 123DateFormat::DateFormat() 124: fCalendar(0), 125 fNumberFormat(0), 126 fCapitalizationContext(UDISPCTX_CAPITALIZATION_NONE) 127{ 128} 129 130//---------------------------------------------------------------------- 131 132DateFormat::DateFormat(const DateFormat& other) 133: Format(other), 134 fCalendar(0), 135 fNumberFormat(0), 136 fCapitalizationContext(UDISPCTX_CAPITALIZATION_NONE) 137{ 138 *this = other; 139} 140 141//---------------------------------------------------------------------- 142 143DateFormat& DateFormat::operator=(const DateFormat& other) 144{ 145 if (this != &other) 146 { 147 delete fCalendar; 148 delete fNumberFormat; 149 if(other.fCalendar) { 150 fCalendar = other.fCalendar->clone(); 151 } else { 152 fCalendar = NULL; 153 } 154 if(other.fNumberFormat) { 155 fNumberFormat = (NumberFormat*)other.fNumberFormat->clone(); 156 } else { 157 fNumberFormat = NULL; 158 } 159 fBoolFlags = other.fBoolFlags; 160 fCapitalizationContext = other.fCapitalizationContext; 161 } 162 return *this; 163} 164 165//---------------------------------------------------------------------- 166 167DateFormat::~DateFormat() 168{ 169 delete fCalendar; 170 delete fNumberFormat; 171} 172 173//---------------------------------------------------------------------- 174 175UBool 176DateFormat::operator==(const Format& other) const 177{ 178 // This protected comparison operator should only be called by subclasses 179 // which have confirmed that the other object being compared against is 180 // an instance of a sublcass of DateFormat. THIS IS IMPORTANT. 181 182 // Format::operator== guarantees that this cast is safe 183 DateFormat* fmt = (DateFormat*)&other; 184 185 return (this == fmt) || 186 (Format::operator==(other) && 187 fCalendar&&(fCalendar->isEquivalentTo(*fmt->fCalendar)) && 188 (fNumberFormat && *fNumberFormat == *fmt->fNumberFormat) && 189 (fCapitalizationContext == fmt->fCapitalizationContext) ); 190} 191 192//---------------------------------------------------------------------- 193 194UnicodeString& 195DateFormat::format(const Formattable& obj, 196 UnicodeString& appendTo, 197 FieldPosition& fieldPosition, 198 UErrorCode& status) const 199{ 200 if (U_FAILURE(status)) return appendTo; 201 202 // if the type of the Formattable is double or long, treat it as if it were a Date 203 UDate date = 0; 204 switch (obj.getType()) 205 { 206 case Formattable::kDate: 207 date = obj.getDate(); 208 break; 209 case Formattable::kDouble: 210 date = (UDate)obj.getDouble(); 211 break; 212 case Formattable::kLong: 213 date = (UDate)obj.getLong(); 214 break; 215 default: 216 status = U_ILLEGAL_ARGUMENT_ERROR; 217 return appendTo; 218 } 219 220 // Is this right? 221 //if (fieldPosition.getBeginIndex() == fieldPosition.getEndIndex()) 222 // status = U_ILLEGAL_ARGUMENT_ERROR; 223 224 return format(date, appendTo, fieldPosition); 225} 226 227//---------------------------------------------------------------------- 228 229UnicodeString& 230DateFormat::format(const Formattable& obj, 231 UnicodeString& appendTo, 232 FieldPositionIterator* posIter, 233 UErrorCode& status) const 234{ 235 if (U_FAILURE(status)) return appendTo; 236 237 // if the type of the Formattable is double or long, treat it as if it were a Date 238 UDate date = 0; 239 switch (obj.getType()) 240 { 241 case Formattable::kDate: 242 date = obj.getDate(); 243 break; 244 case Formattable::kDouble: 245 date = (UDate)obj.getDouble(); 246 break; 247 case Formattable::kLong: 248 date = (UDate)obj.getLong(); 249 break; 250 default: 251 status = U_ILLEGAL_ARGUMENT_ERROR; 252 return appendTo; 253 } 254 255 // Is this right? 256 //if (fieldPosition.getBeginIndex() == fieldPosition.getEndIndex()) 257 // status = U_ILLEGAL_ARGUMENT_ERROR; 258 259 return format(date, appendTo, posIter, status); 260} 261 262//---------------------------------------------------------------------- 263 264// Default implementation for backwards compatibility, subclasses should implement. 265UnicodeString& 266DateFormat::format(Calendar& /* unused cal */, 267 UnicodeString& appendTo, 268 FieldPositionIterator* /* unused posIter */, 269 UErrorCode& status) const { 270 if (U_SUCCESS(status)) { 271 status = U_UNSUPPORTED_ERROR; 272 } 273 return appendTo; 274} 275 276//---------------------------------------------------------------------- 277 278UnicodeString& 279DateFormat::format(UDate date, UnicodeString& appendTo, FieldPosition& fieldPosition) const { 280 if (fCalendar != NULL) { 281 // Use a clone of our calendar instance 282 Calendar* calClone = fCalendar->clone(); 283 if (calClone != NULL) { 284 UErrorCode ec = U_ZERO_ERROR; 285 calClone->setTime(date, ec); 286 if (U_SUCCESS(ec)) { 287 format(*calClone, appendTo, fieldPosition); 288 } 289 delete calClone; 290 } 291 } 292 return appendTo; 293} 294 295//---------------------------------------------------------------------- 296 297UnicodeString& 298DateFormat::format(UDate date, UnicodeString& appendTo, FieldPositionIterator* posIter, 299 UErrorCode& status) const { 300 if (fCalendar != NULL) { 301 Calendar* calClone = fCalendar->clone(); 302 if (calClone != NULL) { 303 calClone->setTime(date, status); 304 if (U_SUCCESS(status)) { 305 format(*calClone, appendTo, posIter, status); 306 } 307 delete calClone; 308 } 309 } 310 return appendTo; 311} 312 313//---------------------------------------------------------------------- 314 315UnicodeString& 316DateFormat::format(UDate date, UnicodeString& appendTo) const 317{ 318 // Note that any error information is just lost. That's okay 319 // for this convenience method. 320 FieldPosition fpos(0); 321 return format(date, appendTo, fpos); 322} 323 324//---------------------------------------------------------------------- 325 326UDate 327DateFormat::parse(const UnicodeString& text, 328 ParsePosition& pos) const 329{ 330 UDate d = 0; // Error return UDate is 0 (the epoch) 331 if (fCalendar != NULL) { 332 Calendar* calClone = fCalendar->clone(); 333 if (calClone != NULL) { 334 int32_t start = pos.getIndex(); 335 calClone->clear(); 336 parse(text, *calClone, pos); 337 if (pos.getIndex() != start) { 338 UErrorCode ec = U_ZERO_ERROR; 339 d = calClone->getTime(ec); 340 if (U_FAILURE(ec)) { 341 // We arrive here if fCalendar => calClone is non-lenient and 342 // there is an out-of-range field. We don't know which field 343 // was illegal so we set the error index to the start. 344 pos.setIndex(start); 345 pos.setErrorIndex(start); 346 d = 0; 347 } 348 } 349 delete calClone; 350 } 351 } 352 return d; 353} 354 355//---------------------------------------------------------------------- 356 357UDate 358DateFormat::parse(const UnicodeString& text, 359 UErrorCode& status) const 360{ 361 if (U_FAILURE(status)) return 0; 362 363 ParsePosition pos(0); 364 UDate result = parse(text, pos); 365 if (pos.getIndex() == 0) { 366#if defined (U_DEBUG_CAL) 367 fprintf(stderr, "%s:%d - - failed to parse - err index %d\n" 368 , __FILE__, __LINE__, pos.getErrorIndex() ); 369#endif 370 status = U_ILLEGAL_ARGUMENT_ERROR; 371 } 372 return result; 373} 374 375//---------------------------------------------------------------------- 376 377void 378DateFormat::parseObject(const UnicodeString& source, 379 Formattable& result, 380 ParsePosition& pos) const 381{ 382 result.setDate(parse(source, pos)); 383} 384 385//---------------------------------------------------------------------- 386 387DateFormat* U_EXPORT2 388DateFormat::createTimeInstance(DateFormat::EStyle style, 389 const Locale& aLocale) 390{ 391 return createDateTimeInstance(kNone, style, aLocale); 392} 393 394//---------------------------------------------------------------------- 395 396DateFormat* U_EXPORT2 397DateFormat::createDateInstance(DateFormat::EStyle style, 398 const Locale& aLocale) 399{ 400 return createDateTimeInstance(style, kNone, aLocale); 401} 402 403//---------------------------------------------------------------------- 404 405DateFormat* U_EXPORT2 406DateFormat::createDateTimeInstance(EStyle dateStyle, 407 EStyle timeStyle, 408 const Locale& aLocale) 409{ 410 if(dateStyle != kNone) 411 { 412 dateStyle = (EStyle) (dateStyle + kDateOffset); 413 } 414 return create(timeStyle, dateStyle, aLocale); 415} 416 417//---------------------------------------------------------------------- 418 419DateFormat* U_EXPORT2 420DateFormat::createInstance() 421{ 422 return createDateTimeInstance(kShort, kShort, Locale::getDefault()); 423} 424 425//---------------------------------------------------------------------- 426 427UnicodeString U_EXPORT2 428DateFormat::getBestPattern( 429 const Locale &locale, 430 const UnicodeString &skeleton, 431 UErrorCode &status) { 432 UnifiedCache *cache = UnifiedCache::getInstance(status); 433 if (U_FAILURE(status)) { 434 return UnicodeString(); 435 } 436 DateFmtBestPatternKey key(locale, skeleton, status); 437 const DateFmtBestPattern *patternPtr = NULL; 438 cache->get(key, patternPtr, status); 439 if (U_FAILURE(status)) { 440 return UnicodeString(); 441 } 442 UnicodeString result(patternPtr->fPattern); 443 patternPtr->removeRef(); 444 return result; 445} 446 447DateFormat* U_EXPORT2 448DateFormat::createInstanceForSkeleton( 449 Calendar *calendarToAdopt, 450 const UnicodeString& skeleton, 451 const Locale &locale, 452 UErrorCode &status) { 453 LocalPointer<Calendar> calendar(calendarToAdopt); 454 if (U_FAILURE(status)) { 455 return NULL; 456 } 457 if (calendar.isNull()) { 458 status = U_ILLEGAL_ARGUMENT_ERROR; 459 return NULL; 460 } 461 DateFormat *result = createInstanceForSkeleton(skeleton, locale, status); 462 if (U_FAILURE(status)) { 463 return NULL; 464 } 465 result->adoptCalendar(calendar.orphan()); 466 return result; 467} 468 469DateFormat* U_EXPORT2 470DateFormat::createInstanceForSkeleton( 471 const UnicodeString& skeleton, 472 const Locale &locale, 473 UErrorCode &status) { 474 if (U_FAILURE(status)) { 475 return NULL; 476 } 477 LocalPointer<DateFormat> df( 478 new SimpleDateFormat( 479 getBestPattern(locale, skeleton, status), 480 locale, status), 481 status); 482 return U_SUCCESS(status) ? df.orphan() : NULL; 483} 484 485DateFormat* U_EXPORT2 486DateFormat::createInstanceForSkeleton( 487 const UnicodeString& skeleton, 488 UErrorCode &status) { 489 return createInstanceForSkeleton( 490 skeleton, Locale::getDefault(), status); 491} 492 493//---------------------------------------------------------------------- 494 495DateFormat* U_EXPORT2 496DateFormat::create(EStyle timeStyle, EStyle dateStyle, const Locale& locale) 497{ 498 UErrorCode status = U_ZERO_ERROR; 499#if U_PLATFORM_HAS_WIN32_API 500 char buffer[8]; 501 int32_t count = locale.getKeywordValue("compat", buffer, sizeof(buffer), status); 502 503 // if the locale has "@compat=host", create a host-specific DateFormat... 504 if (count > 0 && uprv_strcmp(buffer, "host") == 0) { 505 Win32DateFormat *f = new Win32DateFormat(timeStyle, dateStyle, locale, status); 506 507 if (U_SUCCESS(status)) { 508 return f; 509 } 510 511 delete f; 512 } 513#endif 514 515 // is it relative? 516 if(/*((timeStyle!=UDAT_NONE)&&(timeStyle & UDAT_RELATIVE)) || */((dateStyle!=kNone)&&((dateStyle-kDateOffset) & UDAT_RELATIVE))) { 517 RelativeDateFormat *r = new RelativeDateFormat((UDateFormatStyle)timeStyle, (UDateFormatStyle)(dateStyle-kDateOffset), locale, status); 518 if(U_SUCCESS(status)) return r; 519 delete r; 520 status = U_ZERO_ERROR; 521 } 522 523 // Try to create a SimpleDateFormat of the desired style. 524 SimpleDateFormat *f = new SimpleDateFormat(timeStyle, dateStyle, locale, status); 525 if (U_SUCCESS(status)) return f; 526 delete f; 527 528 // If that fails, try to create a format using the default pattern and 529 // the DateFormatSymbols for this locale. 530 status = U_ZERO_ERROR; 531 f = new SimpleDateFormat(locale, status); 532 if (U_SUCCESS(status)) return f; 533 delete f; 534 535 // This should never really happen, because the preceding constructor 536 // should always succeed. If the resource data is unavailable, a last 537 // resort object should be returned. 538 return 0; 539} 540 541//---------------------------------------------------------------------- 542 543const Locale* U_EXPORT2 544DateFormat::getAvailableLocales(int32_t& count) 545{ 546 // Get the list of installed locales. 547 // Even if root has the correct date format for this locale, 548 // it's still a valid locale (we don't worry about data fallbacks). 549 return Locale::getAvailableLocales(count); 550} 551 552//---------------------------------------------------------------------- 553 554void 555DateFormat::adoptCalendar(Calendar* newCalendar) 556{ 557 delete fCalendar; 558 fCalendar = newCalendar; 559} 560 561//---------------------------------------------------------------------- 562void 563DateFormat::setCalendar(const Calendar& newCalendar) 564{ 565 Calendar* newCalClone = newCalendar.clone(); 566 if (newCalClone != NULL) { 567 adoptCalendar(newCalClone); 568 } 569} 570 571//---------------------------------------------------------------------- 572 573const Calendar* 574DateFormat::getCalendar() const 575{ 576 return fCalendar; 577} 578 579//---------------------------------------------------------------------- 580 581void 582DateFormat::adoptNumberFormat(NumberFormat* newNumberFormat) 583{ 584 delete fNumberFormat; 585 fNumberFormat = newNumberFormat; 586 newNumberFormat->setParseIntegerOnly(TRUE); 587} 588//---------------------------------------------------------------------- 589 590void 591DateFormat::setNumberFormat(const NumberFormat& newNumberFormat) 592{ 593 NumberFormat* newNumFmtClone = (NumberFormat*)newNumberFormat.clone(); 594 if (newNumFmtClone != NULL) { 595 adoptNumberFormat(newNumFmtClone); 596 } 597} 598 599//---------------------------------------------------------------------- 600 601const NumberFormat* 602DateFormat::getNumberFormat() const 603{ 604 return fNumberFormat; 605} 606 607//---------------------------------------------------------------------- 608 609void 610DateFormat::adoptTimeZone(TimeZone* zone) 611{ 612 if (fCalendar != NULL) { 613 fCalendar->adoptTimeZone(zone); 614 } 615} 616//---------------------------------------------------------------------- 617 618void 619DateFormat::setTimeZone(const TimeZone& zone) 620{ 621 if (fCalendar != NULL) { 622 fCalendar->setTimeZone(zone); 623 } 624} 625 626//---------------------------------------------------------------------- 627 628const TimeZone& 629DateFormat::getTimeZone() const 630{ 631 if (fCalendar != NULL) { 632 return fCalendar->getTimeZone(); 633 } 634 // If calendar doesn't exists, create default timezone. 635 // fCalendar is rarely null 636 return *(TimeZone::createDefault()); 637} 638 639//---------------------------------------------------------------------- 640 641void 642DateFormat::setLenient(UBool lenient) 643{ 644 if (fCalendar != NULL) { 645 fCalendar->setLenient(lenient); 646 } 647 UErrorCode status = U_ZERO_ERROR; 648 setBooleanAttribute(UDAT_PARSE_ALLOW_WHITESPACE, lenient, status); 649 setBooleanAttribute(UDAT_PARSE_ALLOW_NUMERIC, lenient, status); 650} 651 652//---------------------------------------------------------------------- 653 654UBool 655DateFormat::isLenient() const 656{ 657 UBool lenient = TRUE; 658 if (fCalendar != NULL) { 659 lenient = fCalendar->isLenient(); 660 } 661 UErrorCode status = U_ZERO_ERROR; 662 return lenient 663 && getBooleanAttribute(UDAT_PARSE_ALLOW_WHITESPACE, status) 664 && getBooleanAttribute(UDAT_PARSE_ALLOW_NUMERIC, status); 665} 666 667void 668DateFormat::setCalendarLenient(UBool lenient) 669{ 670 if (fCalendar != NULL) { 671 fCalendar->setLenient(lenient); 672 } 673} 674 675//---------------------------------------------------------------------- 676 677UBool 678DateFormat::isCalendarLenient() const 679{ 680 if (fCalendar != NULL) { 681 return fCalendar->isLenient(); 682 } 683 // fCalendar is rarely null 684 return FALSE; 685} 686 687 688//---------------------------------------------------------------------- 689 690 691void DateFormat::setContext(UDisplayContext value, UErrorCode& status) 692{ 693 if (U_FAILURE(status)) 694 return; 695 if ( (UDisplayContextType)((uint32_t)value >> 8) == UDISPCTX_TYPE_CAPITALIZATION ) { 696 fCapitalizationContext = value; 697 } else { 698 status = U_ILLEGAL_ARGUMENT_ERROR; 699 } 700} 701 702 703//---------------------------------------------------------------------- 704 705 706UDisplayContext DateFormat::getContext(UDisplayContextType type, UErrorCode& status) const 707{ 708 if (U_FAILURE(status)) 709 return (UDisplayContext)0; 710 if (type != UDISPCTX_TYPE_CAPITALIZATION) { 711 status = U_ILLEGAL_ARGUMENT_ERROR; 712 return (UDisplayContext)0; 713 } 714 return fCapitalizationContext; 715} 716 717 718//---------------------------------------------------------------------- 719 720 721DateFormat& 722DateFormat::setBooleanAttribute(UDateFormatBooleanAttribute attr, 723 UBool newValue, 724 UErrorCode &status) { 725 if(!fBoolFlags.isValidValue(newValue)) { 726 status = U_ILLEGAL_ARGUMENT_ERROR; 727 } else { 728 fBoolFlags.set(attr, newValue); 729 } 730 731 return *this; 732} 733 734//---------------------------------------------------------------------- 735 736UBool 737DateFormat::getBooleanAttribute(UDateFormatBooleanAttribute attr, UErrorCode &/*status*/) const { 738 739 return fBoolFlags.get(attr); 740} 741 742U_NAMESPACE_END 743 744#endif /* #if !UCONFIG_NO_FORMATTING */ 745 746//eof 747