1 /* 2 ** Copyright 2003, Axel Dörfler, axeld@pinc-software.de. All rights reserved. 3 ** Distributed under the terms of the OpenBeOS License. 4 */ 5 6 7 #include <AutoDeleter.h> 8 #include <CalendarView.h> 9 #include <Catalog.h> 10 #include <Locale.h> 11 #include <LocaleRoster.h> 12 #include <TimeZone.h> 13 14 #include <unicode/datefmt.h> 15 #include <unicode/dcfmtsym.h> 16 #include <unicode/decimfmt.h> 17 #include <unicode/dtfmtsym.h> 18 #include <unicode/smpdtfmt.h> 19 #include <ICUWrapper.h> 20 21 #include <vector> 22 23 24 #define ICU_VERSION icu_44 25 26 27 using BPrivate::ObjectDeleter; 28 using BPrivate::B_WEEK_START_MONDAY; 29 using BPrivate::B_WEEK_START_SUNDAY; 30 31 32 static DateFormat* CreateDateFormat(bool longFormat, const Locale& locale, 33 const BString& format); 34 static DateFormat* CreateTimeFormat(bool longFormat, const Locale& locale, 35 const BString& format); 36 37 38 BLocale::BLocale(const char* languageAndCountryCode) 39 : 40 fCountry(languageAndCountryCode), 41 fLanguage(languageAndCountryCode), 42 fICULocale(new ICU_VERSION::Locale(languageAndCountryCode)) 43 { 44 } 45 46 47 BLocale::BLocale(const BLocale& other) 48 : 49 fCountry(other.fCountry), 50 fLanguage(other.fLanguage), 51 fICULocale(new ICU_VERSION::Locale(*other.fICULocale)), 52 fLongDateFormat(other.fLongDateFormat), 53 fShortDateFormat(other.fShortDateFormat), 54 fLongTimeFormat(other.fLongTimeFormat), 55 fShortTimeFormat(other.fShortTimeFormat) 56 { 57 } 58 59 60 BLocale& 61 BLocale::operator=(const BLocale& other) 62 { 63 if (this == &other) 64 return *this; 65 66 *fICULocale = *other.fICULocale; 67 68 fLongDateFormat = other.fLongDateFormat; 69 fShortDateFormat = other.fShortDateFormat; 70 fLongTimeFormat = other.fLongTimeFormat; 71 fShortTimeFormat = other.fShortTimeFormat; 72 73 fCountry = other.fCountry; 74 fLanguage = other.fLanguage; 75 76 return *this; 77 } 78 79 80 BLocale::~BLocale() 81 { 82 delete fICULocale; 83 } 84 85 86 const char * 87 BLocale::GetString(uint32 id) 88 { 89 // Note: this code assumes a certain order of the string bases 90 91 if (id >= B_OTHER_STRINGS_BASE) { 92 if (id == B_CODESET) 93 return "UTF-8"; 94 95 return ""; 96 } 97 return fLanguage.GetString(id); 98 } 99 100 101 void 102 BLocale::SetCountry(const BCountry& newCountry) 103 { 104 fCountry = newCountry; 105 } 106 107 108 void 109 BLocale::SetCollator(const BCollator& newCollator) 110 { 111 fCollator = newCollator; 112 } 113 114 115 void 116 BLocale::SetLanguage(const char* languageCode) 117 { 118 fLanguage.SetTo(languageCode); 119 } 120 121 122 const char* 123 BLocale::Code() const 124 { 125 return fICULocale->getName(); 126 } 127 128 129 bool 130 BLocale::GetName(BString& name) const 131 { 132 UnicodeString uString; 133 fICULocale->getDisplayName(uString); 134 BStringByteSink stringConverter(&name); 135 uString.toUTF8(stringConverter); 136 return true; 137 } 138 139 // #pragma mark - Date 140 141 142 status_t 143 BLocale::FormatDate(char* string, size_t maxSize, time_t time, bool longFormat) 144 { 145 ObjectDeleter<DateFormat> dateFormatter = CreateDateFormat(longFormat, 146 *fICULocale, longFormat ? fLongDateFormat : fShortDateFormat); 147 if (dateFormatter.Get() == NULL) 148 return B_NO_MEMORY; 149 150 UnicodeString ICUString; 151 ICUString = dateFormatter->format((UDate)time * 1000, ICUString); 152 153 CheckedArrayByteSink stringConverter(string, maxSize); 154 155 ICUString.toUTF8(stringConverter); 156 157 if (stringConverter.Overflowed()) 158 return B_BAD_VALUE; 159 160 return B_OK; 161 } 162 163 164 status_t 165 BLocale::FormatDate(BString *string, time_t time, bool longFormat) 166 { 167 string->Truncate(0); 168 // We make the string empty, this way even in cases where ICU fail we at 169 // least return something sane 170 ObjectDeleter<DateFormat> dateFormatter = CreateDateFormat(longFormat, 171 *fICULocale, longFormat ? fLongDateFormat : fShortDateFormat); 172 if (dateFormatter.Get() == NULL) 173 return B_NO_MEMORY; 174 175 UnicodeString ICUString; 176 ICUString = dateFormatter->format((UDate)time * 1000, ICUString); 177 178 BStringByteSink stringConverter(string); 179 180 ICUString.toUTF8(stringConverter); 181 182 return B_OK; 183 } 184 185 186 status_t 187 BLocale::FormatDate(BString* string, int*& fieldPositions, int& fieldCount, 188 time_t time, bool longFormat) 189 { 190 string->Truncate(0); 191 192 ObjectDeleter<DateFormat> dateFormatter = CreateDateFormat(longFormat, 193 *fICULocale, longFormat ? fLongDateFormat : fShortDateFormat); 194 if (dateFormatter.Get() == NULL) 195 return B_NO_MEMORY; 196 197 fieldPositions = NULL; 198 UErrorCode error = U_ZERO_ERROR; 199 ICU_VERSION::FieldPositionIterator positionIterator; 200 UnicodeString ICUString; 201 ICUString = dateFormatter->format((UDate)time * 1000, ICUString, 202 &positionIterator, error); 203 204 if (error != U_ZERO_ERROR) 205 return B_ERROR; 206 207 ICU_VERSION::FieldPosition field; 208 std::vector<int> fieldPosStorage; 209 fieldCount = 0; 210 while (positionIterator.next(field)) { 211 fieldPosStorage.push_back(field.getBeginIndex()); 212 fieldPosStorage.push_back(field.getEndIndex()); 213 fieldCount += 2; 214 } 215 216 fieldPositions = (int*) malloc(fieldCount * sizeof(int)); 217 218 for (int i = 0 ; i < fieldCount ; i++ ) 219 fieldPositions[i] = fieldPosStorage[i]; 220 221 BStringByteSink stringConverter(string); 222 223 ICUString.toUTF8(stringConverter); 224 225 return B_OK; 226 } 227 228 229 status_t 230 BLocale::GetDateFormat(BString& format, bool longFormat) const 231 { 232 if (longFormat && fLongDateFormat.Length() > 0) 233 format = fLongDateFormat; 234 else if (!longFormat && fShortDateFormat.Length() > 0) 235 format = fShortDateFormat; 236 else { 237 format.Truncate(0); 238 239 ObjectDeleter<DateFormat> dateFormatter = CreateDateFormat(longFormat, 240 *fICULocale, longFormat ? fLongDateFormat : fShortDateFormat); 241 if (dateFormatter.Get() == NULL) 242 return B_NO_MEMORY; 243 244 SimpleDateFormat* dateFormatterImpl 245 = static_cast<SimpleDateFormat*>(dateFormatter.Get()); 246 247 UnicodeString ICUString; 248 ICUString = dateFormatterImpl->toPattern(ICUString); 249 250 BStringByteSink stringConverter(&format); 251 252 ICUString.toUTF8(stringConverter); 253 } 254 255 return B_OK; 256 } 257 258 259 status_t 260 BLocale::SetDateFormat(const char* formatString, bool longFormat) 261 { 262 if (longFormat) 263 fLongDateFormat = formatString; 264 else 265 fShortDateFormat = formatString; 266 267 return B_OK; 268 } 269 270 271 status_t 272 BLocale::GetDateFields(BDateElement*& fields, int& fieldCount, 273 bool longFormat) const 274 { 275 ObjectDeleter<DateFormat> dateFormatter = CreateDateFormat(longFormat, 276 *fICULocale, longFormat ? fLongDateFormat : fShortDateFormat); 277 if (dateFormatter.Get() == NULL) 278 return B_NO_MEMORY; 279 280 fields = NULL; 281 UErrorCode error = U_ZERO_ERROR; 282 ICU_VERSION::FieldPositionIterator positionIterator; 283 UnicodeString ICUString; 284 time_t now; 285 ICUString = dateFormatter->format((UDate)time(&now) * 1000, ICUString, 286 &positionIterator, error); 287 288 if (U_FAILURE(error)) 289 return B_ERROR; 290 291 ICU_VERSION::FieldPosition field; 292 std::vector<int> fieldPosStorage; 293 fieldCount = 0; 294 while (positionIterator.next(field)) { 295 fieldPosStorage.push_back(field.getField()); 296 fieldCount ++; 297 } 298 299 fields = (BDateElement*) malloc(fieldCount * sizeof(BDateElement)); 300 301 for (int i = 0 ; i < fieldCount ; i++ ) { 302 switch (fieldPosStorage[i]) { 303 case UDAT_YEAR_FIELD: 304 fields[i] = B_DATE_ELEMENT_YEAR; 305 break; 306 case UDAT_MONTH_FIELD: 307 fields[i] = B_DATE_ELEMENT_MONTH; 308 break; 309 case UDAT_DATE_FIELD: 310 fields[i] = B_DATE_ELEMENT_DAY; 311 break; 312 default: 313 fields[i] = B_DATE_ELEMENT_INVALID; 314 break; 315 } 316 } 317 318 return B_OK; 319 } 320 321 322 int 323 BLocale::StartOfWeek() const 324 { 325 UErrorCode err = U_ZERO_ERROR; 326 Calendar* c = Calendar::createInstance(*fICULocale, err); 327 328 if (err == U_ZERO_ERROR && c->getFirstDayOfWeek(err) == UCAL_SUNDAY) { 329 delete c; 330 return B_WEEK_START_SUNDAY; 331 } else { 332 delete c; 333 // Might be another day, but BeAPI will not handle it 334 return B_WEEK_START_MONDAY; 335 } 336 } 337 338 339 status_t 340 BLocale::FormatDateTime(char* target, size_t maxSize, time_t time, 341 bool longFormat) 342 { 343 ObjectDeleter<DateFormat> dateFormatter = CreateDateFormat(longFormat, 344 *fICULocale, longFormat ? fLongDateFormat : fShortDateFormat); 345 if (dateFormatter.Get() == NULL) 346 return B_NO_MEMORY; 347 348 ObjectDeleter<DateFormat> timeFormatter = CreateTimeFormat(longFormat, 349 *fICULocale, longFormat ? fLongTimeFormat : fShortTimeFormat); 350 if (timeFormatter.Get() == NULL) 351 return B_NO_MEMORY; 352 353 UnicodeString ICUString; 354 ICUString = dateFormatter->format((UDate)time * 1000, ICUString); 355 356 ICUString.append(UnicodeString::fromUTF8(", ")); 357 358 ICUString = timeFormatter->format((UDate)time * 1000, ICUString); 359 360 CheckedArrayByteSink stringConverter(target, maxSize); 361 ICUString.toUTF8(stringConverter); 362 363 if (stringConverter.Overflowed()) 364 return B_BAD_VALUE; 365 366 return B_OK; 367 } 368 369 370 // #pragma mark - Time 371 372 373 status_t 374 BLocale::FormatTime(char* string, size_t maxSize, time_t time, bool longFormat) 375 { 376 ObjectDeleter<DateFormat> timeFormatter = CreateTimeFormat(longFormat, 377 *fICULocale, longFormat ? fLongTimeFormat : fShortTimeFormat); 378 if (timeFormatter.Get() == NULL) 379 return B_NO_MEMORY; 380 381 UnicodeString ICUString; 382 ICUString = timeFormatter->format((UDate)time * 1000, ICUString); 383 384 CheckedArrayByteSink stringConverter(string, maxSize); 385 386 ICUString.toUTF8(stringConverter); 387 388 if (stringConverter.Overflowed()) 389 return B_BAD_VALUE; 390 391 return B_OK; 392 } 393 394 395 status_t 396 BLocale::FormatTime(BString* string, time_t time, bool longFormat, 397 const BTimeZone* timeZone) 398 { 399 string->Truncate(0); 400 401 ObjectDeleter<DateFormat> timeFormatter = CreateTimeFormat(longFormat, 402 *fICULocale, longFormat ? fLongTimeFormat : fShortTimeFormat); 403 if (timeFormatter.Get() == NULL) 404 return B_NO_MEMORY; 405 406 if (timeZone != NULL) { 407 ObjectDeleter<TimeZone> icuTimeZone 408 = TimeZone::createTimeZone(timeZone->Code().String()); 409 if (icuTimeZone.Get() == NULL) 410 return B_NO_MEMORY; 411 timeFormatter->setTimeZone(*icuTimeZone.Get()); 412 } 413 414 UnicodeString ICUString; 415 ICUString = timeFormatter->format((UDate)time * 1000, ICUString); 416 417 BStringByteSink stringConverter(string); 418 419 ICUString.toUTF8(stringConverter); 420 421 return B_OK; 422 } 423 424 425 status_t 426 BLocale::FormatTime(BString* string, int*& fieldPositions, int& fieldCount, 427 time_t time, bool longFormat) 428 { 429 string->Truncate(0); 430 431 ObjectDeleter<DateFormat> timeFormatter = CreateTimeFormat(longFormat, 432 *fICULocale, longFormat ? fLongTimeFormat : fShortTimeFormat); 433 if (timeFormatter.Get() == NULL) 434 return B_NO_MEMORY; 435 436 fieldPositions = NULL; 437 UErrorCode error = U_ZERO_ERROR; 438 ICU_VERSION::FieldPositionIterator positionIterator; 439 UnicodeString ICUString; 440 ICUString = timeFormatter->format((UDate)time * 1000, ICUString, 441 &positionIterator, error); 442 443 if (error != U_ZERO_ERROR) 444 return B_ERROR; 445 446 ICU_VERSION::FieldPosition field; 447 std::vector<int> fieldPosStorage; 448 fieldCount = 0; 449 while (positionIterator.next(field)) { 450 fieldPosStorage.push_back(field.getBeginIndex()); 451 fieldPosStorage.push_back(field.getEndIndex()); 452 fieldCount += 2; 453 } 454 455 fieldPositions = (int*) malloc(fieldCount * sizeof(int)); 456 457 for (int i = 0 ; i < fieldCount ; i++ ) 458 fieldPositions[i] = fieldPosStorage[i]; 459 460 BStringByteSink stringConverter(string); 461 462 ICUString.toUTF8(stringConverter); 463 464 return B_OK; 465 } 466 467 468 status_t 469 BLocale::GetTimeFields(BDateElement*& fields, int& fieldCount, 470 bool longFormat) const 471 { 472 ObjectDeleter<DateFormat> timeFormatter = CreateTimeFormat(longFormat, 473 *fICULocale, longFormat ? fLongTimeFormat : fShortTimeFormat); 474 if (timeFormatter.Get() == NULL) 475 return B_NO_MEMORY; 476 477 fields = NULL; 478 UErrorCode error = U_ZERO_ERROR; 479 ICU_VERSION::FieldPositionIterator positionIterator; 480 UnicodeString ICUString; 481 time_t now; 482 ICUString = timeFormatter->format((UDate)time(&now) * 1000, ICUString, 483 &positionIterator, error); 484 485 if (error != U_ZERO_ERROR) 486 return B_ERROR; 487 488 ICU_VERSION::FieldPosition field; 489 std::vector<int> fieldPosStorage; 490 fieldCount = 0; 491 while (positionIterator.next(field)) { 492 fieldPosStorage.push_back(field.getField()); 493 fieldCount ++; 494 } 495 496 fields = (BDateElement*) malloc(fieldCount * sizeof(BDateElement)); 497 498 for (int i = 0 ; i < fieldCount ; i++ ) { 499 switch (fieldPosStorage[i]) { 500 case UDAT_HOUR_OF_DAY1_FIELD: 501 case UDAT_HOUR_OF_DAY0_FIELD: 502 case UDAT_HOUR1_FIELD: 503 case UDAT_HOUR0_FIELD: 504 fields[i] = B_DATE_ELEMENT_HOUR; 505 break; 506 case UDAT_MINUTE_FIELD: 507 fields[i] = B_DATE_ELEMENT_MINUTE; 508 break; 509 case UDAT_SECOND_FIELD: 510 fields[i] = B_DATE_ELEMENT_SECOND; 511 break; 512 case UDAT_AM_PM_FIELD: 513 fields[i] = B_DATE_ELEMENT_AM_PM; 514 break; 515 default: 516 fields[i] = B_DATE_ELEMENT_INVALID; 517 break; 518 } 519 } 520 521 return B_OK; 522 } 523 524 525 status_t 526 BLocale::SetTimeFormat(const char* formatString, bool longFormat) 527 { 528 if (longFormat) 529 fLongTimeFormat = formatString; 530 else 531 fShortTimeFormat = formatString; 532 533 return B_OK; 534 } 535 536 537 status_t 538 BLocale::GetTimeFormat(BString& format, bool longFormat) const 539 { 540 if (longFormat && fLongTimeFormat.Length() > 0) 541 format = fLongTimeFormat; 542 else if (!longFormat && fShortTimeFormat.Length() > 0) 543 format = fShortTimeFormat; 544 else { 545 format.Truncate(0); 546 547 ObjectDeleter<DateFormat> timeFormatter = CreateTimeFormat(longFormat, 548 *fICULocale, longFormat ? fLongTimeFormat : fShortTimeFormat); 549 if (timeFormatter.Get() == NULL) 550 return B_NO_MEMORY; 551 552 SimpleDateFormat* timeFormatterImpl 553 = static_cast<SimpleDateFormat*>(timeFormatter.Get()); 554 555 UnicodeString ICUString; 556 ICUString = timeFormatterImpl->toPattern(ICUString); 557 558 BStringByteSink stringConverter(&format); 559 ICUString.toUTF8(stringConverter); 560 } 561 562 return B_OK; 563 } 564 565 566 // #pragma mark - Numbers 567 568 569 status_t 570 BLocale::FormatNumber(char* string, size_t maxSize, double value) 571 { 572 BString fullString; 573 status_t status = FormatNumber(&fullString, value); 574 if (status == B_OK) 575 strlcpy(string, fullString.String(), maxSize); 576 577 return status; 578 } 579 580 581 status_t 582 BLocale::FormatNumber(BString* string, double value) 583 { 584 UErrorCode err = U_ZERO_ERROR; 585 ObjectDeleter<NumberFormat> numberFormatter = NumberFormat::createInstance( 586 *fICULocale, NumberFormat::kNumberStyle, err); 587 588 if (numberFormatter.Get() == NULL) 589 return B_NO_MEMORY; 590 if (U_FAILURE(err)) 591 return B_ERROR; 592 593 UnicodeString ICUString; 594 ICUString = numberFormatter->format(value, ICUString); 595 596 string->Truncate(0); 597 BStringByteSink stringConverter(string); 598 ICUString.toUTF8(stringConverter); 599 600 return B_OK; 601 } 602 603 604 status_t 605 BLocale::FormatNumber(char* string, size_t maxSize, int32 value) 606 { 607 BString fullString; 608 status_t status = FormatNumber(&fullString, value); 609 if (status == B_OK) 610 strlcpy(string, fullString.String(), maxSize); 611 612 return status; 613 } 614 615 616 status_t 617 BLocale::FormatNumber(BString* string, int32 value) 618 { 619 UErrorCode err = U_ZERO_ERROR; 620 ObjectDeleter<NumberFormat> numberFormatter = NumberFormat::createInstance( 621 *fICULocale, NumberFormat::kNumberStyle, err); 622 623 if (numberFormatter.Get() == NULL) 624 return B_NO_MEMORY; 625 if (U_FAILURE(err)) 626 return B_ERROR; 627 628 UnicodeString ICUString; 629 ICUString = numberFormatter->format((int32_t)value, ICUString); 630 631 string->Truncate(0); 632 BStringByteSink stringConverter(string); 633 ICUString.toUTF8(stringConverter); 634 635 return B_OK; 636 } 637 638 639 ssize_t 640 BLocale::FormatMonetary(char* string, size_t maxSize, double value) 641 { 642 BString fullString; 643 ssize_t written = FormatMonetary(&fullString, value); 644 if (written < 0) 645 return written; 646 647 return strlcpy(string, fullString.String(), maxSize); 648 } 649 650 651 ssize_t 652 BLocale::FormatMonetary(BString* string, double value) 653 { 654 if (string == NULL) 655 return B_BAD_VALUE; 656 657 UErrorCode err; 658 ObjectDeleter<NumberFormat> numberFormatter 659 = NumberFormat::createCurrencyInstance(*fICULocale, err); 660 661 if (numberFormatter.Get() == NULL) 662 return B_NO_MEMORY; 663 if (U_FAILURE(err)) 664 return B_ERROR; 665 666 UnicodeString ICUString; 667 ICUString = numberFormatter->format(value, ICUString); 668 669 string->Truncate(0); 670 BStringByteSink stringConverter(string); 671 ICUString.toUTF8(stringConverter); 672 673 return string->Length(); 674 } 675 676 677 // #pragma mark - Helpers 678 679 680 static DateFormat* 681 CreateDateFormat(bool longFormat, const Locale& locale, 682 const BString& format) 683 { 684 DateFormat* dateFormatter = DateFormat::createDateInstance( 685 longFormat ? DateFormat::FULL : DateFormat::SHORT, locale); 686 687 if (format.Length() > 0) { 688 SimpleDateFormat* dateFormatterImpl 689 = static_cast<SimpleDateFormat*>(dateFormatter); 690 691 UnicodeString pattern(format.String()); 692 dateFormatterImpl->applyPattern(pattern); 693 } 694 695 return dateFormatter; 696 } 697 698 699 static DateFormat* 700 CreateTimeFormat(bool longFormat, const Locale& locale, 701 const BString& format) 702 { 703 DateFormat* timeFormatter = DateFormat::createTimeInstance( 704 longFormat ? DateFormat::MEDIUM : DateFormat::SHORT, locale); 705 706 if (format.Length() > 0) { 707 SimpleDateFormat* timeFormatterImpl 708 = static_cast<SimpleDateFormat*>(timeFormatter); 709 710 UnicodeString pattern(format.String()); 711 timeFormatterImpl->applyPattern(pattern); 712 } 713 714 return timeFormatter; 715 } 716