From a6bc072149ff1a0fac575f4b409ec5e679e7c876 Mon Sep 17 00:00:00 2001 From: Travis Keep Date: Fri, 5 Dec 2014 18:15:35 +0000 Subject: [PATCH] ICU-11401 Reduce cache keyspace by caching only the components of DateFormat by locale rather than actual DateFormat objects. X-SVN-Rev: 36811 --- .gitattributes | 3 +- icu4c/source/i18n/calendar.cpp | 70 +++++- icu4c/source/i18n/datefmt.cpp | 229 +++----------------- icu4c/source/i18n/dtfmtsym.cpp | 46 ++++ icu4c/source/i18n/i18n.vcxproj | 3 +- icu4c/source/i18n/i18n.vcxproj.filters | 5 +- icu4c/source/i18n/sharedcalendar.h | 34 +++ icu4c/source/i18n/shareddatefmt.h | 34 --- icu4c/source/i18n/shareddateformatsymbols.h | 34 +++ icu4c/source/i18n/smpdtfmt.cpp | 46 ++-- icu4c/source/i18n/unicode/calendar.h | 28 +++ icu4c/source/i18n/unicode/dtfmtsym.h | 14 ++ icu4c/source/i18n/unicode/smpdtfmt.h | 8 - icu4c/source/test/intltest/dtfmttst.cpp | 60 +++++ icu4c/source/test/intltest/dtfmttst.h | 3 + 15 files changed, 337 insertions(+), 280 deletions(-) create mode 100644 icu4c/source/i18n/sharedcalendar.h delete mode 100644 icu4c/source/i18n/shareddatefmt.h create mode 100644 icu4c/source/i18n/shareddateformatsymbols.h diff --git a/.gitattributes b/.gitattributes index 3bcf7a3b731..ec2a08dd84d 100644 --- a/.gitattributes +++ b/.gitattributes @@ -77,7 +77,8 @@ icu4c/source/extra/uconv/uconv.vcxproj.filters -text icu4c/source/i18n/i18n.vcxproj -text icu4c/source/i18n/i18n.vcxproj.filters -text icu4c/source/i18n/scientificnumberformatter.cpp -text -icu4c/source/i18n/shareddatefmt.h -text +icu4c/source/i18n/sharedcalendar.h -text +icu4c/source/i18n/shareddateformatsymbols.h -text icu4c/source/i18n/shareddatetimepatterngenerator.h -text icu4c/source/i18n/unicode/scientificnumberformatter.h -text icu4c/source/io/io.vcxproj -text diff --git a/icu4c/source/i18n/calendar.cpp b/icu4c/source/i18n/calendar.cpp index 062a9e9c30a..adee2d66dfb 100644 --- a/icu4c/source/i18n/calendar.cpp +++ b/icu4c/source/i18n/calendar.cpp @@ -57,6 +57,8 @@ #include "ustrenum.h" #include "uassert.h" #include "olsontz.h" +#include "sharedcalendar.h" +#include "unifiedcache.h" #if !UCONFIG_NO_SERVICE static icu::ICULocaleService* gService = NULL; @@ -199,6 +201,27 @@ typedef enum ECalType { U_NAMESPACE_BEGIN +SharedCalendar::~SharedCalendar() { + delete ptr; +} + +template<> U_I18N_API +const SharedCalendar *LocaleCacheKey::createObject( + const void * /*unusedCreationContext*/, UErrorCode &status) const { + Calendar *calendar = Calendar::makeInstance(fLoc, status); + if (U_FAILURE(status)) { + return NULL; + } + SharedCalendar *shared = new SharedCalendar(calendar); + if (shared == NULL) { + delete calendar; + status = U_MEMORY_ALLOCATION_ERROR; + return NULL; + } + shared->addRef(); + return shared; +} + static ECalType getCalendarType(const char *s) { for (int i = 0; gCalTypes[i] != NULL; i++) { if (uprv_stricmp(s, gCalTypes[i]) == 0) { @@ -833,9 +856,8 @@ Calendar::createInstance(const Locale& aLocale, UErrorCode& success) // Note: this is the bottleneck that actually calls the service routines. -Calendar* U_EXPORT2 -Calendar::createInstance(TimeZone* zone, const Locale& aLocale, UErrorCode& success) -{ +Calendar * U_EXPORT2 +Calendar::makeInstance(const Locale& aLocale, UErrorCode& success) { if (U_FAILURE(success)) { return NULL; } @@ -855,7 +877,6 @@ Calendar::createInstance(TimeZone* zone, const Locale& aLocale, UErrorCode& succ Calendar* c = NULL; if(U_FAILURE(success) || !u) { - delete zone; if(U_SUCCESS(success)) { // Propagate some kind of err success = U_INTERNAL_PROGRAM_ERROR; } @@ -884,7 +905,6 @@ Calendar::createInstance(TimeZone* zone, const Locale& aLocale, UErrorCode& succ c = (Calendar*)getCalendarService(success)->get(l, LocaleKey::KIND_ANY, &actualLoc2, success); if(U_FAILURE(success) || !c) { - delete zone; if(U_SUCCESS(success)) { success = U_INTERNAL_PROGRAM_ERROR; // Propagate some err } @@ -910,7 +930,6 @@ Calendar::createInstance(TimeZone* zone, const Locale& aLocale, UErrorCode& succ #endif success = U_MISSING_RESOURCE_ERROR; // requested a calendar type which could NOT be found. delete c; - delete zone; return NULL; } #ifdef U_DEBUG_CALSVC @@ -933,8 +952,27 @@ Calendar::createInstance(TimeZone* zone, const Locale& aLocale, UErrorCode& succ c = (Calendar*)u; } + return c; +} + +Calendar* U_EXPORT2 +Calendar::createInstance(TimeZone* zone, const Locale& aLocale, UErrorCode& success) +{ + LocalPointer zonePtr(zone); + const SharedCalendar *shared = NULL; + UnifiedCache::getByLocale(aLocale, shared, success); + if (U_FAILURE(success)) { + return NULL; + } + Calendar *c = (*shared)->clone(); + shared->removeRef(); + if (c == NULL) { + success = U_MEMORY_ALLOCATION_ERROR; + return NULL; + } + // Now, reset calendar to default state: - c->adoptTimeZone(zone); // Set the correct time zone + c->adoptTimeZone(zonePtr.orphan()); // Set the correct time zone c->setTimeInMillis(getNow(), success); // let the new calendar have the current time. return c; @@ -954,6 +992,24 @@ Calendar::createInstance(const TimeZone& zone, const Locale& aLocale, UErrorCode // ------------------------------------- +void U_EXPORT2 +Calendar::getCalendarTypeFromLocale( + const Locale &aLocale, + char *typeBuffer, + int32_t typeBufferSize, + UErrorCode &success) { + const SharedCalendar *shared = NULL; + UnifiedCache::getByLocale(aLocale, shared, success); + if (U_FAILURE(success)) { + return; + } + uprv_strncpy(typeBuffer, (*shared)->getType(), typeBufferSize); + shared->removeRef(); + if (typeBuffer[typeBufferSize - 1]) { + success = U_BUFFER_OVERFLOW_ERROR; + } +} + UBool Calendar::operator==(const Calendar& that) const { diff --git a/icu4c/source/i18n/datefmt.cpp b/icu4c/source/i18n/datefmt.cpp index e9efbea7565..7006e10317a 100644 --- a/icu4c/source/i18n/datefmt.cpp +++ b/icu4c/source/i18n/datefmt.cpp @@ -27,9 +27,6 @@ #include "unicode/dtptngen.h" #include "unicode/udisplaycontext.h" #include "reldtfmt.h" -#include "shareddatefmt.h" -#include "shareddatetimepatterngenerator.h" -#include "unifiedcache.h" #include "cstring.h" #include "windtfmt.h" @@ -44,181 +41,6 @@ U_NAMESPACE_BEGIN -SharedDateFormat::~SharedDateFormat() { - delete ptr; -} - -// We must fully define LocaleCacheKey -template<> U_I18N_API -const SharedDateFormat *LocaleCacheKey::createObject( - const void * /*creationContext*/, UErrorCode &status) const { - status = U_UNSUPPORTED_ERROR; - return NULL; -} - -class U_I18N_API DateFmtKeyByStyle : public LocaleCacheKey { - private: - DateFormat::EStyle fDateStyle; - DateFormat::EStyle fTimeStyle; - public: - DateFmtKeyByStyle( - const Locale &loc, - DateFormat::EStyle dateStyle, - DateFormat::EStyle timeStyle) - : LocaleCacheKey(loc), - fDateStyle(dateStyle), - fTimeStyle(timeStyle) { } - DateFmtKeyByStyle(const DateFmtKeyByStyle &other) : - LocaleCacheKey(other), - fDateStyle(other.fDateStyle), - fTimeStyle(other.fTimeStyle) { } - virtual ~DateFmtKeyByStyle(); - virtual int32_t hashCode() const { - int32_t hash = 37 * LocaleCacheKey::hashCode() + fDateStyle; - hash = 37 * hash + fTimeStyle; - return hash; - } - virtual UBool operator==(const CacheKeyBase &other) const { - // reflexive - if (this == &other) { - return TRUE; - } - if (!LocaleCacheKey::operator==(other)) { - return FALSE; - } - // We know that this an other are of same class if we get this far. - const DateFmtKeyByStyle *realOther = - static_cast(&other); - return (realOther->fDateStyle == fDateStyle && - realOther->fTimeStyle == fTimeStyle); - } - virtual CacheKeyBase *clone() const { - return new DateFmtKeyByStyle(*this); - } - virtual const SharedDateFormat *createObject( - const void * /*creationContext*/, UErrorCode &status) const { - DateFormat::EStyle dateStyle = fDateStyle; - if(dateStyle != DateFormat::kNone) - { - dateStyle = (DateFormat::EStyle) (dateStyle + DateFormat::kDateOffset); - } - DateFormat *fmt = DateFormat::create(fTimeStyle, dateStyle, fLoc); - if (fmt == NULL) { - status = U_MEMORY_ALLOCATION_ERROR; - return NULL; - } - SharedDateFormat *result = new SharedDateFormat(fmt); - if (result == NULL) { - delete fmt; - status = U_MEMORY_ALLOCATION_ERROR; - return NULL; - } - result->addRef(); - return result; - } -}; - -DateFmtKeyByStyle::~DateFmtKeyByStyle() { -} - -class U_I18N_API DateFmtKeyBySkeleton : public LocaleCacheKey { - private: - UnicodeString fSkeleton; - public: - DateFmtKeyBySkeleton(const Locale &loc, const UnicodeString &skeleton) : - LocaleCacheKey(loc), - fSkeleton(skeleton) { } - DateFmtKeyBySkeleton(const DateFmtKeyBySkeleton &other) : - LocaleCacheKey(other), - fSkeleton(other.fSkeleton) { } - virtual ~DateFmtKeyBySkeleton(); - virtual int32_t hashCode() const { - return 37 * LocaleCacheKey::hashCode() + fSkeleton.hashCode(); - } - virtual UBool operator==(const CacheKeyBase &other) const { - // reflexive - if (this == &other) { - return TRUE; - } - if (!LocaleCacheKey::operator==(other)) { - return FALSE; - } - // We know that this an other are of same class if we get this far. - const DateFmtKeyBySkeleton *realOther = - static_cast(&other); - return (realOther->fSkeleton == fSkeleton); - } - virtual CacheKeyBase *clone() const { - return new DateFmtKeyBySkeleton(*this); - } - virtual const SharedDateFormat *createObject( - const void *creationContext, UErrorCode &status) const { - void *mutableCreationContext = const_cast(creationContext); - DateTimePatternGenerator *ownedDtpg = NULL; - DateTimePatternGenerator *dtpg = - static_cast(mutableCreationContext); - if (dtpg == NULL) { - ownedDtpg = DateTimePatternGenerator::createInstance(fLoc, status); - if (U_FAILURE(status)) { - return NULL; - } - dtpg = ownedDtpg; - } - DateFormat *fmt = new SimpleDateFormat( - dtpg->getBestPattern(fSkeleton, status), - fLoc, - status); - delete ownedDtpg; - if (fmt == NULL) { - status = U_MEMORY_ALLOCATION_ERROR; - return NULL; - } - if (U_FAILURE(status)) { - delete fmt; - return NULL; - } - SharedDateFormat *result = new SharedDateFormat(fmt); - if (result == NULL) { - delete fmt; - status = U_MEMORY_ALLOCATION_ERROR; - return NULL; - } - result->addRef(); - return result; - } -}; - -DateFmtKeyBySkeleton::~DateFmtKeyBySkeleton() { -} - -static DateFormat *createFromCache( - const CacheKey &key, - const void *context, - UErrorCode &status) { - const UnifiedCache *cache = UnifiedCache::getInstance(status); - if (U_FAILURE(status)) { - return NULL; - } - const SharedDateFormat *ptr = NULL; - cache->get(key, context, ptr, status); - if (U_FAILURE(status)) { - return NULL; - } - DateFormat *result = static_cast((*ptr)->clone()); - ptr->removeRef(); - if (result == NULL) { - status = U_MEMORY_ALLOCATION_ERROR; - } else { - // Set the currently active default TimeZone, - // because the cached instance might be created - // with another TimeZone. - // Note: We could do some optimization in SharedDateFormat - // to pick up the current default without cloning old default. - result->adoptTimeZone(TimeZone::createDefault()); - } - return result; -} - DateFormat::DateFormat() : fCalendar(0), fNumberFormat(0), @@ -487,9 +309,7 @@ DateFormat* U_EXPORT2 DateFormat::createTimeInstance(DateFormat::EStyle style, const Locale& aLocale) { - DateFmtKeyByStyle key(aLocale, kNone, style); - UErrorCode status = U_ZERO_ERROR; - return createFromCache(key, NULL, status); + return createDateTimeInstance(kNone, style, aLocale); } //---------------------------------------------------------------------- @@ -498,9 +318,7 @@ DateFormat* U_EXPORT2 DateFormat::createDateInstance(DateFormat::EStyle style, const Locale& aLocale) { - DateFmtKeyByStyle key(aLocale, style, kNone); - UErrorCode status = U_ZERO_ERROR; - return createFromCache(key, NULL, status); + return createDateTimeInstance(style, kNone, aLocale); } //---------------------------------------------------------------------- @@ -510,9 +328,11 @@ DateFormat::createDateTimeInstance(EStyle dateStyle, EStyle timeStyle, const Locale& aLocale) { - DateFmtKeyByStyle key(aLocale, dateStyle, timeStyle); - UErrorCode status = U_ZERO_ERROR; - return createFromCache(key, NULL, status); + if(dateStyle != kNone) + { + dateStyle = (EStyle) (dateStyle + kDateOffset); + } + return create(timeStyle, dateStyle, aLocale); } //---------------------------------------------------------------------- @@ -520,9 +340,7 @@ DateFormat::createDateTimeInstance(EStyle dateStyle, DateFormat* U_EXPORT2 DateFormat::createInstance() { - DateFmtKeyByStyle key(Locale::getDefault(), kShort, kShort); - UErrorCode status = U_ZERO_ERROR; - return createFromCache(key, NULL, status); + return createDateTimeInstance(kShort, kShort, Locale::getDefault()); } //---------------------------------------------------------------------- @@ -541,8 +359,7 @@ DateFormat::createInstanceForSkeleton( status = U_ILLEGAL_ARGUMENT_ERROR; return NULL; } - DateFmtKeyBySkeleton key(locale, skeleton); - DateFormat *result = createFromCache(key, NULL, status); + DateFormat *result = createInstanceForSkeleton(skeleton, locale, status); if (U_FAILURE(status)) { return NULL; } @@ -555,22 +372,21 @@ DateFormat::createInstanceForSkeleton( const UnicodeString& skeleton, const Locale &locale, UErrorCode &status) { + LocalPointer gen( + DateTimePatternGenerator::createInstance(locale, status)); if (U_FAILURE(status)) { return NULL; } - DateFmtKeyBySkeleton key(locale, skeleton); - return createFromCache(key, NULL, status); + return internalCreateInstanceForSkeleton( + skeleton, locale, *gen, status); } DateFormat* U_EXPORT2 DateFormat::createInstanceForSkeleton( const UnicodeString& skeleton, UErrorCode &status) { - if (U_FAILURE(status)) { - return NULL; - } - DateFmtKeyBySkeleton key(Locale::getDefault(), skeleton); - return createFromCache(key, NULL, status); + return createInstanceForSkeleton( + skeleton, Locale::getDefault(), status); } DateFormat* U_EXPORT2 @@ -582,8 +398,19 @@ DateFormat::internalCreateInstanceForSkeleton( if (U_FAILURE(status)) { return NULL; } - DateFmtKeyBySkeleton key(locale, skeleton); - return createFromCache(key, &gen, status); + DateFormat *fmt = new SimpleDateFormat( + gen.getBestPattern(skeleton, status), + locale, + status); + if (fmt == NULL) { + status = U_MEMORY_ALLOCATION_ERROR; + return NULL; + } + if (U_FAILURE(status)) { + delete fmt; + return NULL; + } + return fmt; } //---------------------------------------------------------------------- diff --git a/icu4c/source/i18n/dtfmtsym.cpp b/icu4c/source/i18n/dtfmtsym.cpp index a08811b75a3..f2ac6f75c22 100644 --- a/icu4c/source/i18n/dtfmtsym.cpp +++ b/icu4c/source/i18n/dtfmtsym.cpp @@ -36,6 +36,9 @@ #include "hash.h" #include "uresimp.h" #include "ureslocs.h" +#include "shareddateformatsymbols.h" +#include "unicode/calendar.h" +#include "unifiedcache.h" // ***************************************************************************** // class DateFormatSymbols @@ -146,6 +149,32 @@ typedef enum LastResortSize { U_NAMESPACE_BEGIN +SharedDateFormatSymbols::~SharedDateFormatSymbols() { +} + +template<> U_I18N_API +const SharedDateFormatSymbols * + LocaleCacheKey::createObject( + const void */*unusedContext*/, UErrorCode &status) const { + char type[256]; + Calendar::getCalendarTypeFromLocale(fLoc, type, UPRV_LENGTHOF(type), status); + if (U_FAILURE(status)) { + return NULL; + } + SharedDateFormatSymbols *shared + = new SharedDateFormatSymbols(fLoc, type, status); + if (shared == NULL) { + status = U_MEMORY_ALLOCATION_ERROR; + return NULL; + } + if (U_FAILURE(status)) { + delete shared; + return NULL; + } + shared->addRef(); + return shared; +} + UOBJECT_DEFINE_RTTI_IMPLEMENTATION(DateFormatSymbols) #define kSUPPLEMENTAL "supplementalData" @@ -191,6 +220,23 @@ static inline UnicodeString* newUnicodeStringArray(size_t count) { //------------------------------------------------------ +DateFormatSymbols * U_EXPORT2 +DateFormatSymbols::createForLocale( + const Locale& locale, UErrorCode &status) { + const SharedDateFormatSymbols *shared = NULL; + UnifiedCache::getByLocale(locale, shared, status); + if (U_FAILURE(status)) { + return NULL; + } + DateFormatSymbols *result = new DateFormatSymbols(shared->get()); + shared->removeRef(); + if (result == NULL) { + status = U_MEMORY_ALLOCATION_ERROR; + return NULL; + } + return result; +} + DateFormatSymbols::DateFormatSymbols(const Locale& locale, UErrorCode& status) : UObject() diff --git a/icu4c/source/i18n/i18n.vcxproj b/icu4c/source/i18n/i18n.vcxproj index bb06144bf83..a339aa9f2c3 100644 --- a/icu4c/source/i18n/i18n.vcxproj +++ b/icu4c/source/i18n/i18n.vcxproj @@ -1152,7 +1152,8 @@ - + + diff --git a/icu4c/source/i18n/i18n.vcxproj.filters b/icu4c/source/i18n/i18n.vcxproj.filters index f960acc2f26..f1252f7d325 100644 --- a/icu4c/source/i18n/i18n.vcxproj.filters +++ b/icu4c/source/i18n/i18n.vcxproj.filters @@ -698,7 +698,10 @@ formatting - + + formatting + + formatting diff --git a/icu4c/source/i18n/sharedcalendar.h b/icu4c/source/i18n/sharedcalendar.h new file mode 100644 index 00000000000..2a10c88b593 --- /dev/null +++ b/icu4c/source/i18n/sharedcalendar.h @@ -0,0 +1,34 @@ +/* +****************************************************************************** +* Copyright (C) 2014, International Business Machines +* Corporation and others. All Rights Reserved. +****************************************************************************** +* sharedcalendar.h +*/ + +#ifndef __SHARED_CALENDAR_H__ +#define __SHARED_CALENDAR_H__ + +#include "unicode/utypes.h" +#include "sharedobject.h" + +U_NAMESPACE_BEGIN + +class Calendar; + +class U_I18N_API SharedCalendar : public SharedObject { +public: + SharedCalendar(Calendar *calToAdopt) : ptr(calToAdopt) { } + virtual ~SharedCalendar(); + const Calendar *get() const { return ptr; } + const Calendar *operator->() const { return ptr; } + const Calendar &operator*() const { return *ptr; } +private: + Calendar *ptr; + SharedCalendar(const SharedCalendar &); + SharedCalendar &operator=(const SharedCalendar &); +}; + +U_NAMESPACE_END + +#endif diff --git a/icu4c/source/i18n/shareddatefmt.h b/icu4c/source/i18n/shareddatefmt.h deleted file mode 100644 index 010b6e2c0fb..00000000000 --- a/icu4c/source/i18n/shareddatefmt.h +++ /dev/null @@ -1,34 +0,0 @@ -/* -****************************************************************************** -* Copyright (C) 2014, International Business Machines -* Corporation and others. All Rights Reserved. -****************************************************************************** -* shareddateformat.h -*/ - -#ifndef __SHARED_DATEFORMAT_H__ -#define __SHARED_DATEFORMAT_H__ - -#include "unicode/utypes.h" -#include "sharedobject.h" - -U_NAMESPACE_BEGIN - -class DateFormat; - -class U_I18N_API SharedDateFormat : public SharedObject { -public: - SharedDateFormat(DateFormat *dfToAdopt) : ptr(dfToAdopt) { } - virtual ~SharedDateFormat(); - const DateFormat *get() const { return ptr; } - const DateFormat *operator->() const { return ptr; } - const DateFormat &operator*() const { return *ptr; } -private: - DateFormat *ptr; - SharedDateFormat(const SharedDateFormat &); - SharedDateFormat &operator=(const SharedDateFormat &); -}; - -U_NAMESPACE_END - -#endif diff --git a/icu4c/source/i18n/shareddateformatsymbols.h b/icu4c/source/i18n/shareddateformatsymbols.h new file mode 100644 index 00000000000..8451183bc29 --- /dev/null +++ b/icu4c/source/i18n/shareddateformatsymbols.h @@ -0,0 +1,34 @@ +/* +****************************************************************************** +* Copyright (C) 2014, International Business Machines +* Corporation and others. All Rights Reserved. +****************************************************************************** +* shareddateformatsymbols.h +*/ + +#ifndef __SHARED_DATEFORMATSYMBOLS_H__ +#define __SHARED_DATEFORMATSYMBOLS_H__ + +#include "unicode/utypes.h" +#include "sharedobject.h" +#include "unicode/dtfmtsym.h" + +U_NAMESPACE_BEGIN + + +class U_I18N_API SharedDateFormatSymbols : public SharedObject { +public: + SharedDateFormatSymbols( + const Locale &loc, const char *type, UErrorCode &status) + : dfs(loc, type, status) { } + virtual ~SharedDateFormatSymbols(); + const DateFormatSymbols &get() const { return dfs; } +private: + DateFormatSymbols dfs; + SharedDateFormatSymbols(const SharedDateFormatSymbols &); + SharedDateFormatSymbols &operator=(const SharedDateFormatSymbols &); +}; + +U_NAMESPACE_END + +#endif diff --git a/icu4c/source/i18n/smpdtfmt.cpp b/icu4c/source/i18n/smpdtfmt.cpp index 749484e71be..4ba8d27a760 100644 --- a/icu4c/source/i18n/smpdtfmt.cpp +++ b/icu4c/source/i18n/smpdtfmt.cpp @@ -394,7 +394,8 @@ SimpleDateFormat::SimpleDateFormat(const UnicodeString& pattern, fDateOverride.setToBogus(); fTimeOverride.setToBogus(); initializeBooleanAttributes(); - initializeSymbols(fLocale, initializeCalendar(NULL,fLocale,status), status); + initializeCalendar(NULL,fLocale,status); + fSymbols = DateFormatSymbols::createForLocale(fLocale, status); initialize(fLocale, status); initializeDefaultCentury(); @@ -414,7 +415,8 @@ SimpleDateFormat::SimpleDateFormat(const UnicodeString& pattern, fDateOverride.setTo(override); fTimeOverride.setToBogus(); initializeBooleanAttributes(); - initializeSymbols(fLocale, initializeCalendar(NULL,fLocale,status), status); + initializeCalendar(NULL,fLocale,status); + fSymbols = DateFormatSymbols::createForLocale(fLocale, status); initialize(fLocale, status); initializeDefaultCentury(); @@ -438,7 +440,8 @@ SimpleDateFormat::SimpleDateFormat(const UnicodeString& pattern, fTimeOverride.setToBogus(); initializeBooleanAttributes(); - initializeSymbols(fLocale, initializeCalendar(NULL,fLocale,status), status); + initializeCalendar(NULL,fLocale,status); + fSymbols = DateFormatSymbols::createForLocale(fLocale, status); initialize(fLocale, status); initializeDefaultCentury(); } @@ -460,7 +463,8 @@ SimpleDateFormat::SimpleDateFormat(const UnicodeString& pattern, fTimeOverride.setToBogus(); initializeBooleanAttributes(); - initializeSymbols(fLocale, initializeCalendar(NULL,fLocale,status), status); + initializeCalendar(NULL,fLocale,status); + fSymbols = DateFormatSymbols::createForLocale(fLocale, status); initialize(fLocale, status); initializeDefaultCentury(); @@ -550,7 +554,8 @@ SimpleDateFormat::SimpleDateFormat(const Locale& locale, { if (U_FAILURE(status)) return; initializeBooleanAttributes(); - initializeSymbols(fLocale, initializeCalendar(NULL, fLocale, status),status); + initializeCalendar(NULL, fLocale, status); + fSymbols = DateFormatSymbols::createForLocale(fLocale, status); if (U_FAILURE(status)) { status = U_ZERO_ERROR; @@ -699,7 +704,7 @@ void SimpleDateFormat::construct(EStyle timeStyle, ures_getLocaleByType(dateTimePatterns, ULOC_ACTUAL_LOCALE, &status)); // create a symbols object from the locale - initializeSymbols(locale,fCalendar, status); + fSymbols = DateFormatSymbols::createForLocale(locale, status); if (U_FAILURE(status)) return; /* test for NULL */ if (fSymbols == 0) { @@ -870,28 +875,9 @@ SimpleDateFormat::initializeCalendar(TimeZone* adoptZone, const Locale& locale, if(!U_FAILURE(status)) { fCalendar = Calendar::createInstance(adoptZone?adoptZone:TimeZone::createDefault(), locale, status); } - if (U_SUCCESS(status) && fCalendar == NULL) { - status = U_MEMORY_ALLOCATION_ERROR; - } return fCalendar; } -void -SimpleDateFormat::initializeSymbols(const Locale& locale, Calendar* calendar, UErrorCode& status) -{ - if(U_FAILURE(status)) { - fSymbols = NULL; - } else { - // pass in calendar type - use NULL (default) if no calendar set (or err). - fSymbols = new DateFormatSymbols(locale, calendar?calendar->getType() :NULL , status); - // Null pointer check - if (fSymbols == NULL) { - status = U_MEMORY_ALLOCATION_ERROR; - return; - } - } -} - void SimpleDateFormat::initialize(const Locale& locale, UErrorCode& status) @@ -3517,10 +3503,16 @@ SimpleDateFormat::setTimeZoneFormat(const TimeZoneFormat& newTimeZoneFormat) void SimpleDateFormat::adoptCalendar(Calendar* calendarToAdopt) { UErrorCode status = U_ZERO_ERROR; + Locale calLocale(fLocale); + calLocale.setKeywordValue("calendar", calendarToAdopt->getType(), status); + DateFormatSymbols *newSymbols = + DateFormatSymbols::createForLocale(calLocale, status); + if (U_FAILURE(status)) { + return; + } DateFormat::adoptCalendar(calendarToAdopt); delete fSymbols; - fSymbols=NULL; - initializeSymbols(fLocale, fCalendar, status); // we need new symbols + fSymbols = newSymbols; initializeDefaultCentury(); // we need a new century (possibly) } diff --git a/icu4c/source/i18n/unicode/calendar.h b/icu4c/source/i18n/unicode/calendar.h index b3f0fd8302a..66b3ef98990 100644 --- a/icu4c/source/i18n/unicode/calendar.h +++ b/icu4c/source/i18n/unicode/calendar.h @@ -2461,6 +2461,34 @@ private: * @return TRUE if a transition is found. */ UBool getImmediatePreviousZoneTransition(UDate base, UDate *transitionTime, UErrorCode& status) const; + +public: + /** + * Creates a new Calendar from a Locale for the cache. + * This method does not set the time or timezone in returned calendar. + * @param locale the locale. + * @param status any error returned here. + * @return the new Calendar object with no time or timezone set. + * @internal For ICU use only. + */ + static Calendar U_EXPORT2 *makeInstance( + const Locale &locale, UErrorCode &status); + + /** + * Get the calendar type for given locale. + * @param locale the locale + * @param typeBuffer calendar type returned here + * @param typeBufferSize The size of typeBuffer in bytes. If the type + * can't fit in the buffer, this method sets status to + * U_BUFFER_OVERFLOW_ERROR + * @param status error, if any, returned here. + * @internal For ICU use only. + */ + static void U_EXPORT2 getCalendarTypeFromLocale( + const Locale &locale, + char *typeBuffer, + int32_t typeBufferSize, + UErrorCode &status); }; // ------------------------------------- diff --git a/icu4c/source/i18n/unicode/dtfmtsym.h b/icu4c/source/i18n/unicode/dtfmtsym.h index 8010914685f..37b0a6d4d9b 100644 --- a/icu4c/source/i18n/unicode/dtfmtsym.h +++ b/icu4c/source/i18n/unicode/dtfmtsym.h @@ -902,6 +902,20 @@ private: * Returns TRUE if c (repeated count times) is the pattern character for a numeric field. */ static UBool U_EXPORT2 isNumericPatternChar(UChar c, int32_t count); +public: + /** + * Gets a DateFormatSymbols by locale. + * Unlike the constructors which always use gregorian calendar, this + * method uses the calendar in the locale. If the locale contains no + * explicit calendar, this method uses the default calendar for that + * locale. + * @param locale the locale. + * @param status error returned here. + * @return the new DateFormatSymbols which the caller owns. + * @internal For ICU use only. + */ + static DateFormatSymbols * U_EXPORT2 createForLocale( + const Locale &locale, UErrorCode &status); }; U_NAMESPACE_END diff --git a/icu4c/source/i18n/unicode/smpdtfmt.h b/icu4c/source/i18n/unicode/smpdtfmt.h index 585ea10a171..3f1e7d79644 100644 --- a/icu4c/source/i18n/unicode/smpdtfmt.h +++ b/icu4c/source/i18n/unicode/smpdtfmt.h @@ -1310,14 +1310,6 @@ private: */ Calendar *initializeCalendar(TimeZone* adoptZone, const Locale& locale, UErrorCode& status); - /** - * initializes fSymbols from parameters. - * @param locale Locale of the symbols - * @param calendar Alias to Calendar that will be used. - * @param status Error code - */ - void initializeSymbols(const Locale& locale, Calendar* calendar, UErrorCode& status); - /** * Called by several of the constructors to load pattern data and formatting symbols * out of a resource bundle and initialize the locale based on it. diff --git a/icu4c/source/test/intltest/dtfmttst.cpp b/icu4c/source/test/intltest/dtfmttst.cpp index 2a602ecb34b..e15fa9e8e3e 100644 --- a/icu4c/source/test/intltest/dtfmttst.cpp +++ b/icu4c/source/test/intltest/dtfmttst.cpp @@ -108,6 +108,9 @@ void DateFormatTest::runIndexedTest( int32_t index, UBool exec, const char* &nam TESTCASE_AUTO(TestCreateInstanceForSkeleton); TESTCASE_AUTO(TestCreateInstanceForSkeletonDefault); TESTCASE_AUTO(TestCreateInstanceForSkeletonWithCalendar); + TESTCASE_AUTO(TestDFSCreateForLocaleNonGregorianLocale); + TESTCASE_AUTO(TestDFSCreateForLocaleWithCalendarInLocale); + TESTCASE_AUTO(TestChangeCalendar); TESTCASE_AUTO_END; } @@ -4686,6 +4689,63 @@ void DateFormatTest::TestCreateInstanceForSkeletonWithCalendar() { assertSuccess("", status); } +void DateFormatTest::TestDFSCreateForLocaleNonGregorianLocale() { + UErrorCode status = U_ZERO_ERROR; + Locale fa("fa"); + LocalPointer sym( + DateFormatSymbols::createForLocale(fa, status)); + if (!assertSuccess("", status)) { + return; + } + + // Farsi should default to the persian calendar, not gregorian + int32_t count; + const UnicodeString *months = sym->getShortMonths(count); + + // First persian month. + UnicodeString expected("\\u0641\\u0631\\u0648\\u0631\\u062f\\u06cc\\u0646"); + assertEquals("", expected.unescape(), months[0]); +} + +void DateFormatTest::TestDFSCreateForLocaleWithCalendarInLocale() { + UErrorCode status = U_ZERO_ERROR; + Locale en_heb("en@calendar=hebrew"); + LocalPointer sym( + DateFormatSymbols::createForLocale(en_heb, status)); + if (!assertSuccess("", status)) { + return; + } + + // We should get the months of the hebrew calendar, not the gregorian + // calendar. + int32_t count; + const UnicodeString *months = sym->getShortMonths(count); + + // First hebrew month. + UnicodeString expected("Tishri"); + assertEquals("", expected, months[0]); +} + +void DateFormatTest::TestChangeCalendar() { + UErrorCode status = U_ZERO_ERROR; + Locale en("en"); + Locale en_heb("en@calendar=hebrew"); + LocalPointer fmt( + DateFormat::createInstanceForSkeleton("yMMMd", en, status)); + if (!assertSuccess("", status)) { + return; + } + fmt->adoptCalendar(Calendar::createInstance(en_heb, status)); + if (!assertSuccess("", status)) { + return; + } + UnicodeString result; + FieldPosition pos(0); + fmt->format(date(98, 5-1, 25), result, pos); + assertEquals("format yMMMd", "Iyar 29, 5758", result); +} + + #endif /* #if !UCONFIG_NO_FORMATTING */ //eof diff --git a/icu4c/source/test/intltest/dtfmttst.h b/icu4c/source/test/intltest/dtfmttst.h index ae165d5fee8..2c3ef5c9950 100644 --- a/icu4c/source/test/intltest/dtfmttst.h +++ b/icu4c/source/test/intltest/dtfmttst.h @@ -244,6 +244,9 @@ public: void TestCreateInstanceForSkeleton(); void TestCreateInstanceForSkeletonDefault(); void TestCreateInstanceForSkeletonWithCalendar(); + void TestDFSCreateForLocaleNonGregorianLocale(); + void TestDFSCreateForLocaleWithCalendarInLocale(); + void TestChangeCalendar(); private: UBool showParse(DateFormat &format, const UnicodeString &formattedString);