diff --git a/src/common/TimeZoneUtil.cpp b/src/common/TimeZoneUtil.cpp index d0984db0eef..dda8c48a7ac 100644 --- a/src/common/TimeZoneUtil.cpp +++ b/src/common/TimeZoneUtil.cpp @@ -36,6 +36,8 @@ #include "../common/os/os_utils.h" #include "unicode/ucal.h" +#include + using namespace Firebird; namespace @@ -45,8 +47,19 @@ namespace public: TimeZoneDesc(MemoryPool& pool) : asciiName(pool), - unicodeName(pool) + unicodeName(pool), + calendar(nullptr) + { + } + + ~TimeZoneDesc() { + UCalendar* val = calendar.exchange(nullptr); + if (val) + { + auto& icuLib = Jrd::UnicodeUtil::getConversionICU(); + icuLib.ucalClose(val); + } } public: @@ -70,9 +83,26 @@ namespace return unicodeName.begin(); } + UCalendar* getCalendar(const Jrd::UnicodeUtil::ConversionICU& icuLib, UErrorCode* err) const + { + UCalendar* val = calendar.load(); + if (!val) + { + val = icuLib.ucalOpen(getUnicodeName(), -1, NULL, UCAL_GREGORIAN, err); + if (!val) + return val; + + UCalendar* old = calendar.exchange(val); + if (old) + return old; + } + return icuLib.ucalClone(val, err); + } + private: string asciiName; Array unicodeName; + mutable std::atomic calendar; }; } @@ -593,8 +623,7 @@ void TimeZoneUtil::extractOffset(const ISC_TIMESTAMP_TZ& timeStampTz, SSHORT* of Jrd::UnicodeUtil::ConversionICU& icuLib = Jrd::UnicodeUtil::getConversionICU(); - UCalendar* icuCalendar = icuLib.ucalOpen( - getDesc(timeStampTz.time_zone)->getUnicodeName(), -1, NULL, UCAL_GREGORIAN, &icuErrorCode); + UCalendar* icuCalendar = getDesc(timeStampTz.time_zone)->getCalendar(icuLib, &icuErrorCode); if (!icuCalendar) status_exception::raise(Arg::Gds(isc_random) << "Error calling ICU's ucal_open."); @@ -702,8 +731,7 @@ void TimeZoneUtil::localTimeStampToUtc(ISC_TIMESTAMP_TZ& timeStampTz) Jrd::UnicodeUtil::ConversionICU& icuLib = Jrd::UnicodeUtil::getConversionICU(); - UCalendar* icuCalendar = icuLib.ucalOpen( - getDesc(timeStampTz.time_zone)->getUnicodeName(), -1, NULL, UCAL_GREGORIAN, &icuErrorCode); + UCalendar* icuCalendar = getDesc(timeStampTz.time_zone)->getCalendar(icuLib, &icuErrorCode); if (!icuCalendar) status_exception::raise(Arg::Gds(isc_random) << "Error calling ICU's ucal_open."); @@ -771,8 +799,7 @@ bool TimeZoneUtil::decodeTimeStamp(const ISC_TIMESTAMP_TZ& timeStampTz, bool gmt #endif Jrd::UnicodeUtil::ConversionICU& icuLib = Jrd::UnicodeUtil::getConversionICU(); - UCalendar* icuCalendar = icuLib.ucalOpen( - getDesc(timeStampTz.time_zone)->getUnicodeName(), -1, NULL, UCAL_GREGORIAN, &icuErrorCode); + UCalendar* icuCalendar = getDesc(timeStampTz.time_zone)->getCalendar(icuLib, &icuErrorCode); if (!icuCalendar) status_exception::raise(Arg::Gds(isc_random) << "Error calling ICU's ucal_open."); @@ -1067,7 +1094,7 @@ TimeZoneRuleIterator::TimeZoneRuleIterator(USHORT aId, const ISC_TIMESTAMP_TZ& a { UErrorCode icuErrorCode = U_ZERO_ERROR; - icuCalendar = icuLib.ucalOpen(getDesc(id)->getUnicodeName(), -1, NULL, UCAL_GREGORIAN, &icuErrorCode); + icuCalendar = getDesc(id)->getCalendar(icuLib, &icuErrorCode); if (!icuCalendar) status_exception::raise(Arg::Gds(isc_random) << "Error calling ICU's ucal_open."); diff --git a/src/common/unicode_util.cpp b/src/common/unicode_util.cpp index 41931e6489b..bd1df9da63d 100644 --- a/src/common/unicode_util.cpp +++ b/src/common/unicode_util.cpp @@ -446,6 +446,7 @@ class ImplementConversionICU : public UnicodeUtil::ConversionICU, BaseICU getEntryPoint("ucal_getTZDataVersion", inModule, ucalGetTZDataVersion); getEntryPoint("ucal_getDefaultTimeZone", inModule, ucalGetDefaultTimeZone); getEntryPoint("ucal_open", inModule, ucalOpen); + getEntryPoint("ucal_clone", inModule, ucalClone); getEntryPoint("ucal_close", inModule, ucalClose); getEntryPoint("ucal_setAttribute", inModule, ucalSetAttribute); getEntryPoint("ucal_setMillis", inModule, ucalSetMillis); diff --git a/src/common/unicode_util.h b/src/common/unicode_util.h index 09de723045b..402924ec91a 100644 --- a/src/common/unicode_util.h +++ b/src/common/unicode_util.h @@ -130,6 +130,7 @@ class UnicodeUtil int32_t (U_EXPORT2* ucalGetDefaultTimeZone) (UChar* result, int32_t resultCapacity, UErrorCode* ec); UCalendar* (U_EXPORT2* ucalOpen) (const UChar* zoneID, int32_t len, const char* locale, UCalendarType type, UErrorCode* err); + UCalendar* (U_EXPORT2* ucalClone)(const UCalendar* cal, UErrorCode* status); void (U_EXPORT2* ucalClose) (UCalendar* cal); void (U_EXPORT2* ucalSetAttribute) (UCalendar* cal, UCalendarAttribute attr, int32_t newValue); void (U_EXPORT2* ucalSetMillis) (UCalendar* cal, UDate dateTime, UErrorCode* err);