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