@@ -45,10 +45,20 @@ namespace
4545 public:
4646 TimeZoneDesc (MemoryPool& pool)
4747 : asciiName(pool),
48- unicodeName (pool)
48+ unicodeName (pool),
49+ icuCachedCalendar(nullptr )
4950 {
5051 }
5152
53+ ~TimeZoneDesc ()
54+ {
55+ if (const auto calendar = icuCachedCalendar.exchange (nullptr ))
56+ {
57+ auto & icuLib = Jrd::UnicodeUtil::getConversionICU ();
58+ icuLib.ucalClose (calendar);
59+ }
60+ }
61+
5262 public:
5363 void setName (const char * name)
5464 {
@@ -70,9 +80,21 @@ namespace
7080 return unicodeName.begin ();
7181 }
7282
83+ IcuCalendarWrapper getCalendar (const Jrd::UnicodeUtil::ConversionICU& icuLib, UErrorCode* err = nullptr ) const
84+ {
85+ auto calendar = icuCachedCalendar.exchange (nullptr );
86+ UErrorCode internalErr = U_ZERO_ERROR;
87+
88+ if (!calendar)
89+ calendar = icuLib.ucalOpen (getUnicodeName (), -1 , nullptr , UCAL_GREGORIAN, (err ? err : &internalErr));
90+
91+ return IcuCalendarWrapper (calendar, &icuCachedCalendar);
92+ }
93+
7394 private:
7495 string asciiName;
7596 Array<UChar> unicodeName;
97+ mutable std::atomic<UCalendar*> icuCachedCalendar;
7698 };
7799}
78100
@@ -594,30 +616,21 @@ void TimeZoneUtil::extractOffset(const ISC_TIMESTAMP_TZ& timeStampTz, SSHORT* of
594616
595617 Jrd::UnicodeUtil::ConversionICU& icuLib = Jrd::UnicodeUtil::getConversionICU ();
596618
597- UCalendar* icuCalendar = icuLib.ucalOpen (
598- getDesc (timeStampTz.time_zone )->getUnicodeName (), -1 , NULL , UCAL_GREGORIAN, &icuErrorCode);
619+ auto icuCalendar = getDesc (timeStampTz.time_zone )->getCalendar (icuLib, &icuErrorCode);
599620
600621 if (!icuCalendar)
601622 status_exception::raise (Arg::Gds (isc_random) << " Error calling ICU's ucal_open." );
602623
603624 icuLib.ucalSetMillis (icuCalendar, timeStampToIcuDate (timeStampTz.utc_timestamp ), &icuErrorCode);
604625
605626 if (U_FAILURE (icuErrorCode))
606- {
607- icuLib.ucalClose (icuCalendar);
608627 status_exception::raise (Arg::Gds (isc_random) << " Error calling ICU's ucal_setMillis." );
609- }
610628
611629 displacement = (icuLib.ucalGet (icuCalendar, UCAL_ZONE_OFFSET, &icuErrorCode) +
612630 icuLib.ucalGet (icuCalendar, UCAL_DST_OFFSET, &icuErrorCode)) / U_MILLIS_PER_MINUTE;
613631
614632 if (U_FAILURE (icuErrorCode))
615- {
616- icuLib.ucalClose (icuCalendar);
617633 status_exception::raise (Arg::Gds (isc_random) << " Error calling ICU's ucal_get." );
618- }
619-
620- icuLib.ucalClose (icuCalendar);
621634 }
622635
623636 *offset = displacement;
@@ -689,8 +702,7 @@ void TimeZoneUtil::localTimeStampToUtc(ISC_TIMESTAMP_TZ& timeStampTz)
689702
690703 Jrd::UnicodeUtil::ConversionICU& icuLib = Jrd::UnicodeUtil::getConversionICU ();
691704
692- UCalendar* icuCalendar = icuLib.ucalOpen (
693- getDesc (timeStampTz.time_zone )->getUnicodeName (), -1 , NULL , UCAL_GREGORIAN, &icuErrorCode);
705+ auto icuCalendar = getDesc (timeStampTz.time_zone )->getCalendar (icuLib, &icuErrorCode);
694706
695707 if (!icuCalendar)
696708 status_exception::raise (Arg::Gds (isc_random) << " Error calling ICU's ucal_open." );
@@ -702,21 +714,13 @@ void TimeZoneUtil::localTimeStampToUtc(ISC_TIMESTAMP_TZ& timeStampTz)
702714 times.tm_hour , times.tm_min , times.tm_sec , &icuErrorCode);
703715
704716 if (U_FAILURE (icuErrorCode))
705- {
706- icuLib.ucalClose (icuCalendar);
707717 status_exception::raise (Arg::Gds (isc_random) << " Error calling ICU's ucal_setDateTime." );
708- }
709718
710719 displacement = (icuLib.ucalGet (icuCalendar, UCAL_ZONE_OFFSET, &icuErrorCode) +
711720 icuLib.ucalGet (icuCalendar, UCAL_DST_OFFSET, &icuErrorCode)) / U_MILLIS_PER_MINUTE;
712721
713722 if (U_FAILURE (icuErrorCode))
714- {
715- icuLib.ucalClose (icuCalendar);
716723 status_exception::raise (Arg::Gds (isc_random) << " Error calling ICU's ucal_get." );
717- }
718-
719- icuLib.ucalClose (icuCalendar);
720724 }
721725
722726 const auto ticks = TimeStamp::timeStampToTicks (timeStampTz.utc_timestamp ) -
@@ -758,30 +762,21 @@ bool TimeZoneUtil::decodeTimeStamp(const ISC_TIMESTAMP_TZ& timeStampTz, bool gmt
758762#endif
759763 Jrd::UnicodeUtil::ConversionICU& icuLib = Jrd::UnicodeUtil::getConversionICU ();
760764
761- UCalendar* icuCalendar = icuLib.ucalOpen (
762- getDesc (timeStampTz.time_zone )->getUnicodeName (), -1 , NULL , UCAL_GREGORIAN, &icuErrorCode);
765+ auto icuCalendar = getDesc (timeStampTz.time_zone )->getCalendar (icuLib, &icuErrorCode);
763766
764767 if (!icuCalendar)
765768 status_exception::raise (Arg::Gds (isc_random) << " Error calling ICU's ucal_open." );
766769
767770 icuLib.ucalSetMillis (icuCalendar, timeStampToIcuDate (timeStampTz.utc_timestamp ), &icuErrorCode);
768771
769772 if (U_FAILURE (icuErrorCode))
770- {
771- icuLib.ucalClose (icuCalendar);
772773 status_exception::raise (Arg::Gds (isc_random) << " Error calling ICU's ucal_setMillis." );
773- }
774774
775775 displacement = (icuLib.ucalGet (icuCalendar, UCAL_ZONE_OFFSET, &icuErrorCode) +
776776 icuLib.ucalGet (icuCalendar, UCAL_DST_OFFSET, &icuErrorCode)) / U_MILLIS_PER_MINUTE;
777777
778778 if (U_FAILURE (icuErrorCode))
779- {
780- icuLib.ucalClose (icuCalendar);
781779 status_exception::raise (Arg::Gds (isc_random) << " Error calling ICU's ucal_get." );
782- }
783-
784- icuLib.ucalClose (icuCalendar);
785780 }
786781 catch (const Exception&)
787782 {
@@ -1050,12 +1045,11 @@ ISC_TIMESTAMP_TZ TimeZoneUtil::dateToTimeStampTz(const ISC_DATE& date, Callbacks
10501045TimeZoneRuleIterator::TimeZoneRuleIterator (USHORT aId, const ISC_TIMESTAMP_TZ& aFrom, const ISC_TIMESTAMP_TZ& aTo)
10511046 : id(aId),
10521047 icuLib(Jrd::UnicodeUtil::getConversionICU()),
1053- toTicks(TimeStamp::timeStampToTicks(aTo.utc_timestamp))
1048+ toTicks(TimeStamp::timeStampToTicks(aTo.utc_timestamp)),
1049+ icuCalendar(getDesc(aId)->getCalendar(icuLib))
10541050{
10551051 UErrorCode icuErrorCode = U_ZERO_ERROR;
10561052
1057- icuCalendar = icuLib.ucalOpen (getDesc (id)->getUnicodeName (), -1 , NULL , UCAL_GREGORIAN, &icuErrorCode);
1058-
10591053 if (!icuCalendar)
10601054 status_exception::raise (Arg::Gds (isc_random) << " Error calling ICU's ucal_open." );
10611055
@@ -1092,11 +1086,6 @@ TimeZoneRuleIterator::TimeZoneRuleIterator(USHORT aId, const ISC_TIMESTAMP_TZ& a
10921086 startTicks = TimeStamp::timeStampToTicks (TimeZoneUtil::icuDateToTimeStamp (icuDate));
10931087}
10941088
1095- TimeZoneRuleIterator::~TimeZoneRuleIterator ()
1096- {
1097- icuLib.ucalClose (icuCalendar);
1098- }
1099-
11001089bool TimeZoneRuleIterator::next ()
11011090{
11021091 if (startTicks > toTicks)
0 commit comments