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