1/* 2******************************************************************************* 3* Copyright (C) 1996-2009, International Business Machines 4* Corporation and others. All Rights Reserved. 5******************************************************************************* 6*/ 7 8#include "unicode/utypes.h" 9 10#if !UCONFIG_NO_FORMATTING 11 12#include "unicode/ucal.h" 13#include "unicode/uloc.h" 14#include "unicode/calendar.h" 15#include "unicode/timezone.h" 16#include "unicode/gregocal.h" 17#include "unicode/simpletz.h" 18#include "unicode/ustring.h" 19#include "unicode/strenum.h" 20#include "cmemory.h" 21#include "cstring.h" 22#include "ustrenum.h" 23#include "uenumimp.h" 24#include "ulist.h" 25 26U_NAMESPACE_USE 27 28static TimeZone* 29_createTimeZone(const UChar* zoneID, int32_t len, UErrorCode* ec) { 30 TimeZone* zone = NULL; 31 if (ec!=NULL && U_SUCCESS(*ec)) { 32 // Note that if zoneID is invalid, we get back GMT. This odd 33 // behavior is by design and goes back to the JDK. The only 34 // failure we will see is a memory allocation failure. 35 int32_t l = (len<0 ? u_strlen(zoneID) : len); 36 UnicodeString zoneStrID; 37 zoneStrID.setTo((UBool)(len < 0), zoneID, l); /* temporary read-only alias */ 38 zone = TimeZone::createTimeZone(zoneStrID); 39 if (zone == NULL) { 40 *ec = U_MEMORY_ALLOCATION_ERROR; 41 } 42 } 43 return zone; 44} 45 46U_CAPI UEnumeration* U_EXPORT2 47ucal_openTimeZones(UErrorCode* ec) { 48 return uenum_openFromStringEnumeration(TimeZone::createEnumeration(), ec); 49} 50 51U_CAPI UEnumeration* U_EXPORT2 52ucal_openCountryTimeZones(const char* country, UErrorCode* ec) { 53 return uenum_openFromStringEnumeration(TimeZone::createEnumeration(country), ec); 54} 55 56U_CAPI int32_t U_EXPORT2 57ucal_getDefaultTimeZone(UChar* result, int32_t resultCapacity, UErrorCode* ec) { 58 int32_t len = 0; 59 if (ec!=NULL && U_SUCCESS(*ec)) { 60 TimeZone* zone = TimeZone::createDefault(); 61 if (zone == NULL) { 62 *ec = U_MEMORY_ALLOCATION_ERROR; 63 } else { 64 UnicodeString id; 65 zone->getID(id); 66 delete zone; 67 len = id.extract(result, resultCapacity, *ec); 68 } 69 } 70 return len; 71} 72 73U_CAPI void U_EXPORT2 74ucal_setDefaultTimeZone(const UChar* zoneID, UErrorCode* ec) { 75 TimeZone* zone = _createTimeZone(zoneID, -1, ec); 76 if (zone != NULL) { 77 TimeZone::adoptDefault(zone); 78 } 79} 80 81U_CAPI int32_t U_EXPORT2 82ucal_getDSTSavings(const UChar* zoneID, UErrorCode* ec) { 83 int32_t result = 0; 84 TimeZone* zone = _createTimeZone(zoneID, -1, ec); 85 if (U_SUCCESS(*ec)) { 86 if (zone->getDynamicClassID() == SimpleTimeZone::getStaticClassID()) { 87 result = ((SimpleTimeZone*) zone)->getDSTSavings(); 88 } else { 89 // Since there is no getDSTSavings on TimeZone, we use a 90 // heuristic: Starting with the current time, march 91 // forwards for one year, looking for DST savings. 92 // Stepping by weeks is sufficient. 93 UDate d = Calendar::getNow(); 94 for (int32_t i=0; i<53; ++i, d+=U_MILLIS_PER_DAY*7.0) { 95 int32_t raw, dst; 96 zone->getOffset(d, FALSE, raw, dst, *ec); 97 if (U_FAILURE(*ec)) { 98 break; 99 } else if (dst != 0) { 100 result = dst; 101 break; 102 } 103 } 104 } 105 } 106 delete zone; 107 return result; 108} 109 110U_CAPI UDate U_EXPORT2 111ucal_getNow() 112{ 113 114 return Calendar::getNow(); 115} 116 117#define ULOC_LOCALE_IDENTIFIER_CAPACITY (ULOC_FULLNAME_CAPACITY + 1 + ULOC_KEYWORD_AND_VALUES_CAPACITY) 118 119U_CAPI UCalendar* U_EXPORT2 120ucal_open( const UChar* zoneID, 121 int32_t len, 122 const char* locale, 123 UCalendarType caltype, 124 UErrorCode* status) 125{ 126 127 if(U_FAILURE(*status)) return 0; 128 129 TimeZone* zone = (zoneID==NULL) ? TimeZone::createDefault() 130 : _createTimeZone(zoneID, len, status); 131 132 if (U_FAILURE(*status)) { 133 return NULL; 134 } 135 136 if ( caltype == UCAL_GREGORIAN ) { 137 char localeBuf[ULOC_LOCALE_IDENTIFIER_CAPACITY]; 138 uprv_strncpy(localeBuf, locale, ULOC_LOCALE_IDENTIFIER_CAPACITY); 139 uloc_setKeywordValue("calendar", "gregorian", localeBuf, ULOC_LOCALE_IDENTIFIER_CAPACITY, status); 140 if (U_FAILURE(*status)) { 141 return NULL; 142 } 143 return (UCalendar*)Calendar::createInstance(zone, Locale(localeBuf), *status); 144 } 145 return (UCalendar*)Calendar::createInstance(zone, Locale(locale), *status); 146} 147 148U_CAPI void U_EXPORT2 149ucal_close(UCalendar *cal) 150{ 151 152 delete (Calendar*) cal; 153} 154 155U_CAPI UCalendar* U_EXPORT2 156ucal_clone(const UCalendar* cal, 157 UErrorCode* status) 158{ 159 if(U_FAILURE(*status)) return 0; 160 161 Calendar* res = ((Calendar*)cal)->clone(); 162 163 if(res == 0) { 164 *status = U_MEMORY_ALLOCATION_ERROR; 165 return 0; 166 } 167 168 return (UCalendar*) res; 169} 170 171U_CAPI void U_EXPORT2 172ucal_setTimeZone( UCalendar* cal, 173 const UChar* zoneID, 174 int32_t len, 175 UErrorCode *status) 176{ 177 178 if(U_FAILURE(*status)) 179 return; 180 181 TimeZone* zone = (zoneID==NULL) ? TimeZone::createDefault() 182 : _createTimeZone(zoneID, len, status); 183 184 if (zone != NULL) { 185 ((Calendar*)cal)->adoptTimeZone(zone); 186 } 187} 188 189U_CAPI int32_t U_EXPORT2 190ucal_getTimeZoneDisplayName(const UCalendar* cal, 191 UCalendarDisplayNameType type, 192 const char *locale, 193 UChar* result, 194 int32_t resultLength, 195 UErrorCode* status) 196{ 197 198 if(U_FAILURE(*status)) return -1; 199 200 const TimeZone& tz = ((Calendar*)cal)->getTimeZone(); 201 UnicodeString id; 202 if(!(result==NULL && resultLength==0)) { 203 // NULL destination for pure preflighting: empty dummy string 204 // otherwise, alias the destination buffer 205 id.setTo(result, 0, resultLength); 206 } 207 208 switch(type) { 209 case UCAL_STANDARD: 210 tz.getDisplayName(FALSE, TimeZone::LONG, Locale(locale), id); 211 break; 212 213 case UCAL_SHORT_STANDARD: 214 tz.getDisplayName(FALSE, TimeZone::SHORT, Locale(locale), id); 215 break; 216 217 case UCAL_DST: 218 tz.getDisplayName(TRUE, TimeZone::LONG, Locale(locale), id); 219 break; 220 221 case UCAL_SHORT_DST: 222 tz.getDisplayName(TRUE, TimeZone::SHORT, Locale(locale), id); 223 break; 224 } 225 226 return id.extract(result, resultLength, *status); 227} 228 229U_CAPI UBool U_EXPORT2 230ucal_inDaylightTime( const UCalendar* cal, 231 UErrorCode* status ) 232{ 233 234 if(U_FAILURE(*status)) return (UBool) -1; 235 return ((Calendar*)cal)->inDaylightTime(*status); 236} 237 238U_CAPI void U_EXPORT2 239ucal_setGregorianChange(UCalendar *cal, UDate date, UErrorCode *pErrorCode) { 240 if(U_FAILURE(*pErrorCode)) { 241 return; 242 } 243 Calendar *cpp_cal = (Calendar *)cal; 244 if(cpp_cal->getDynamicClassID() != GregorianCalendar::getStaticClassID()) { 245 *pErrorCode = U_UNSUPPORTED_ERROR; 246 return; 247 } 248 ((GregorianCalendar *)cpp_cal)->setGregorianChange(date, *pErrorCode); 249} 250 251U_CAPI UDate U_EXPORT2 252ucal_getGregorianChange(const UCalendar *cal, UErrorCode *pErrorCode) { 253 if(U_FAILURE(*pErrorCode)) { 254 return (UDate)0; 255 } 256 Calendar *cpp_cal = (Calendar *)cal; 257 if(cpp_cal->getDynamicClassID() != GregorianCalendar::getStaticClassID()) { 258 *pErrorCode = U_UNSUPPORTED_ERROR; 259 return (UDate)0; 260 } 261 return ((GregorianCalendar *)cpp_cal)->getGregorianChange(); 262} 263 264U_CAPI int32_t U_EXPORT2 265ucal_getAttribute( const UCalendar* cal, 266 UCalendarAttribute attr) 267{ 268 269 switch(attr) { 270 case UCAL_LENIENT: 271 return ((Calendar*)cal)->isLenient(); 272 273 case UCAL_FIRST_DAY_OF_WEEK: 274 return ((Calendar*)cal)->getFirstDayOfWeek(); 275 276 case UCAL_MINIMAL_DAYS_IN_FIRST_WEEK: 277 return ((Calendar*)cal)->getMinimalDaysInFirstWeek(); 278 279 default: 280 break; 281 } 282 return -1; 283} 284 285U_CAPI void U_EXPORT2 286ucal_setAttribute( UCalendar* cal, 287 UCalendarAttribute attr, 288 int32_t newValue) 289{ 290 291 switch(attr) { 292 case UCAL_LENIENT: 293 ((Calendar*)cal)->setLenient((UBool)newValue); 294 break; 295 296 case UCAL_FIRST_DAY_OF_WEEK: 297 ((Calendar*)cal)->setFirstDayOfWeek((UCalendarDaysOfWeek)newValue); 298 break; 299 300 case UCAL_MINIMAL_DAYS_IN_FIRST_WEEK: 301 ((Calendar*)cal)->setMinimalDaysInFirstWeek((uint8_t)newValue); 302 break; 303 } 304} 305 306U_CAPI const char* U_EXPORT2 307ucal_getAvailable(int32_t index) 308{ 309 310 return uloc_getAvailable(index); 311} 312 313U_CAPI int32_t U_EXPORT2 314ucal_countAvailable() 315{ 316 317 return uloc_countAvailable(); 318} 319 320U_CAPI UDate U_EXPORT2 321ucal_getMillis( const UCalendar* cal, 322 UErrorCode* status) 323{ 324 325 if(U_FAILURE(*status)) return (UDate) 0; 326 327 return ((Calendar*)cal)->getTime(*status); 328} 329 330U_CAPI void U_EXPORT2 331ucal_setMillis( UCalendar* cal, 332 UDate dateTime, 333 UErrorCode* status ) 334{ 335 if(U_FAILURE(*status)) return; 336 337 ((Calendar*)cal)->setTime(dateTime, *status); 338} 339 340// TBD: why does this take an UErrorCode? 341U_CAPI void U_EXPORT2 342ucal_setDate( UCalendar* cal, 343 int32_t year, 344 int32_t month, 345 int32_t date, 346 UErrorCode *status) 347{ 348 349 if(U_FAILURE(*status)) return; 350 351 ((Calendar*)cal)->set(year, month, date); 352} 353 354// TBD: why does this take an UErrorCode? 355U_CAPI void U_EXPORT2 356ucal_setDateTime( UCalendar* cal, 357 int32_t year, 358 int32_t month, 359 int32_t date, 360 int32_t hour, 361 int32_t minute, 362 int32_t second, 363 UErrorCode *status) 364{ 365 if(U_FAILURE(*status)) return; 366 367 ((Calendar*)cal)->set(year, month, date, hour, minute, second); 368} 369 370U_CAPI UBool U_EXPORT2 371ucal_equivalentTo( const UCalendar* cal1, 372 const UCalendar* cal2) 373{ 374 375 return ((Calendar*)cal1)->isEquivalentTo(*((Calendar*)cal2)); 376} 377 378U_CAPI void U_EXPORT2 379ucal_add( UCalendar* cal, 380 UCalendarDateFields field, 381 int32_t amount, 382 UErrorCode* status) 383{ 384 385 if(U_FAILURE(*status)) return; 386 387 ((Calendar*)cal)->add(field, amount, *status); 388} 389 390U_CAPI void U_EXPORT2 391ucal_roll( UCalendar* cal, 392 UCalendarDateFields field, 393 int32_t amount, 394 UErrorCode* status) 395{ 396 397 if(U_FAILURE(*status)) return; 398 399 ((Calendar*)cal)->roll(field, amount, *status); 400} 401 402U_CAPI int32_t U_EXPORT2 403ucal_get( const UCalendar* cal, 404 UCalendarDateFields field, 405 UErrorCode* status ) 406{ 407 408 if(U_FAILURE(*status)) return -1; 409 410 return ((Calendar*)cal)->get(field, *status); 411} 412 413U_CAPI void U_EXPORT2 414ucal_set( UCalendar* cal, 415 UCalendarDateFields field, 416 int32_t value) 417{ 418 419 ((Calendar*)cal)->set(field, value); 420} 421 422U_CAPI UBool U_EXPORT2 423ucal_isSet( const UCalendar* cal, 424 UCalendarDateFields field) 425{ 426 427 return ((Calendar*)cal)->isSet(field); 428} 429 430U_CAPI void U_EXPORT2 431ucal_clearField( UCalendar* cal, 432 UCalendarDateFields field) 433{ 434 435 ((Calendar*)cal)->clear(field); 436} 437 438U_CAPI void U_EXPORT2 439ucal_clear(UCalendar* calendar) 440{ 441 442 ((Calendar*)calendar)->clear(); 443} 444 445U_CAPI int32_t U_EXPORT2 446ucal_getLimit( const UCalendar* cal, 447 UCalendarDateFields field, 448 UCalendarLimitType type, 449 UErrorCode *status) 450{ 451 452 if(status==0 || U_FAILURE(*status)) { 453 return -1; 454 } 455 456 switch(type) { 457 case UCAL_MINIMUM: 458 return ((Calendar*)cal)->getMinimum(field); 459 460 case UCAL_MAXIMUM: 461 return ((Calendar*)cal)->getMaximum(field); 462 463 case UCAL_GREATEST_MINIMUM: 464 return ((Calendar*)cal)->getGreatestMinimum(field); 465 466 case UCAL_LEAST_MAXIMUM: 467 return ((Calendar*)cal)->getLeastMaximum(field); 468 469 case UCAL_ACTUAL_MINIMUM: 470 return ((Calendar*)cal)->getActualMinimum(field, 471 *status); 472 473 case UCAL_ACTUAL_MAXIMUM: 474 return ((Calendar*)cal)->getActualMaximum(field, 475 *status); 476 477 default: 478 break; 479 } 480 return -1; 481} 482 483U_CAPI const char * U_EXPORT2 484ucal_getLocaleByType(const UCalendar *cal, ULocDataLocaleType type, UErrorCode* status) 485{ 486 if (cal == NULL) { 487 if (U_SUCCESS(*status)) { 488 *status = U_ILLEGAL_ARGUMENT_ERROR; 489 } 490 return NULL; 491 } 492 return ((Calendar*)cal)->getLocaleID(type, *status); 493} 494 495U_CAPI const char * U_EXPORT2 496ucal_getTZDataVersion(UErrorCode* status) 497{ 498 return TimeZone::getTZDataVersion(*status); 499} 500 501U_CAPI int32_t U_EXPORT2 502ucal_getCanonicalTimeZoneID(const UChar* id, int32_t len, 503 UChar* result, int32_t resultCapacity, UBool *isSystemID, UErrorCode* status) { 504 if(status == 0 || U_FAILURE(*status)) { 505 return 0; 506 } 507 if (isSystemID) { 508 *isSystemID = FALSE; 509 } 510 if (id == 0 || len == 0 || result == 0 || resultCapacity <= 0) { 511 *status = U_ILLEGAL_ARGUMENT_ERROR; 512 return 0; 513 } 514 int32_t reslen = 0; 515 UnicodeString canonical; 516 UBool systemID = FALSE; 517 TimeZone::getCanonicalID(UnicodeString(id, len), canonical, systemID, *status); 518 if (U_SUCCESS(*status)) { 519 if (isSystemID) { 520 *isSystemID = systemID; 521 } 522 reslen = canonical.extract(result, resultCapacity, *status); 523 } 524 return reslen; 525} 526 527U_CAPI const char * U_EXPORT2 528ucal_getType(const UCalendar *cal, UErrorCode* status) 529{ 530 if (U_FAILURE(*status)) { 531 return NULL; 532 } 533 return ((Calendar*)cal)->getType(); 534} 535 536static const UEnumeration defaultKeywordValues = { 537 NULL, 538 NULL, 539 ulist_close_keyword_values_iterator, 540 ulist_count_keyword_values, 541 uenum_unextDefault, 542 ulist_next_keyword_value, 543 ulist_reset_keyword_values_iterator 544}; 545 546static const char * const CAL_TYPES[] = { 547 "gregorian", 548 "japanese", 549 "buddhist", 550 "roc", 551 "persian", 552 "islamic-civil", 553 "islamic", 554 "hebrew", 555 "chinese", 556 "indian", 557 "coptic", 558 "ethiopic", 559 "ethiopic-amete-alem", 560 NULL 561}; 562 563U_CAPI UEnumeration* U_EXPORT2 564ucal_getKeywordValuesForLocale(const char * /* key */, const char* locale, UBool commonlyUsed, UErrorCode *status) { 565 // Resolve region 566 char prefRegion[ULOC_FULLNAME_CAPACITY] = ""; 567 int32_t prefRegionLength = 0; 568 prefRegionLength = uloc_getCountry(locale, prefRegion, sizeof(prefRegion), status); 569 if (prefRegionLength == 0) { 570 char loc[ULOC_FULLNAME_CAPACITY] = ""; 571 int32_t locLength = 0; 572 locLength = uloc_addLikelySubtags(locale, loc, sizeof(loc), status); 573 574 prefRegionLength = uloc_getCountry(loc, prefRegion, sizeof(prefRegion), status); 575 } 576 577 // Read preferred calendar values from supplementalData calendarPreference 578 UResourceBundle *rb = ures_openDirect(NULL, "supplementalData", status); 579 ures_getByKey(rb, "calendarPreferenceData", rb, status); 580 UResourceBundle *order = ures_getByKey(rb, prefRegion, NULL, status); 581 if (*status == U_MISSING_RESOURCE_ERROR && rb != NULL) { 582 *status = U_ZERO_ERROR; 583 order = ures_getByKey(rb, "001", NULL, status); 584 } 585 586 // Create a list of calendar type strings 587 UList *values = NULL; 588 if (U_SUCCESS(*status)) { 589 values = ulist_createEmptyList(status); 590 if (U_SUCCESS(*status)) { 591 for (int i = 0; i < ures_getSize(order); i++) { 592 int32_t len; 593 const UChar *type = ures_getStringByIndex(order, i, &len, status); 594 char *caltype = (char*)uprv_malloc(len + 1); 595 if (caltype == NULL) { 596 *status = U_MEMORY_ALLOCATION_ERROR; 597 break; 598 } 599 u_UCharsToChars(type, caltype, len); 600 *(caltype + len) = 0; 601 602 ulist_addItemEndList(values, caltype, TRUE, status); 603 if (U_FAILURE(*status)) { 604 break; 605 } 606 } 607 608 if (U_SUCCESS(*status) && !commonlyUsed) { 609 // If not commonlyUsed, add other available values 610 for (int32_t i = 0; CAL_TYPES[i] != NULL; i++) { 611 if (!ulist_containsString(values, CAL_TYPES[i], uprv_strlen(CAL_TYPES[i]))) { 612 ulist_addItemEndList(values, CAL_TYPES[i], FALSE, status); 613 if (U_FAILURE(*status)) { 614 break; 615 } 616 } 617 } 618 } 619 if (U_FAILURE(*status)) { 620 ulist_deleteList(values); 621 values = NULL; 622 } 623 } 624 } 625 626 ures_close(order); 627 ures_close(rb); 628 629 if (U_FAILURE(*status) || values == NULL) { 630 return NULL; 631 } 632 633 // Create string enumeration 634 UEnumeration *en = (UEnumeration*)uprv_malloc(sizeof(UEnumeration)); 635 if (en == NULL) { 636 *status = U_MEMORY_ALLOCATION_ERROR; 637 ulist_deleteList(values); 638 return NULL; 639 } 640 ulist_resetList(values); 641 memcpy(en, &defaultKeywordValues, sizeof(UEnumeration)); 642 en->context = values; 643 return en; 644} 645 646#endif /* #if !UCONFIG_NO_FORMATTING */ 647