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