LibUnicode: Add a cache for ICU calendar objects

Analogous to our existing locale and time zone caches.
This commit is contained in:
Timothy Flynn 2026-03-06 12:29:47 -05:00 committed by Jelle Raaijmakers
parent a41f2f56a8
commit b6699d439a
3 changed files with 65 additions and 10 deletions

View file

@ -627,16 +627,7 @@ static void apply_time_zone_to_formatter(icu::SimpleDateFormat& formatter, icu::
auto* calendar = icu::Calendar::createInstance(time_zone_data->time_zone(), locale, status);
verify_icu_success(status);
if (calendar->getDynamicClassID() == icu::GregorianCalendar::getStaticClassID()) {
// https://tc39.es/ecma262/#sec-time-values-and-time-range
// A time value supports a slightly smaller range of -8,640,000,000,000,000 to 8,640,000,000,000,000 milliseconds.
static constexpr double ECMA_262_MINIMUM_TIME = -8.64E15;
auto* gregorian_calendar = static_cast<icu::GregorianCalendar*>(calendar);
gregorian_calendar->setGregorianChange(ECMA_262_MINIMUM_TIME, status);
verify_icu_success(status);
}
CalendarData::adjust_time_range_for_proleptic_calendar(*calendar);
formatter.adoptCalendar(calendar);
}

View file

@ -10,6 +10,7 @@
#include <LibUnicode/ICU.h>
#include <unicode/dtptngen.h>
#include <unicode/gregocal.h>
#include <unicode/locdspnm.h>
#include <unicode/numsys.h>
#include <unicode/tznames.h>
@ -17,6 +18,7 @@
namespace Unicode {
static HashMap<String, OwnPtr<LocaleData>> s_locale_cache;
static HashMap<String, OwnPtr<CalendarData>> s_calendar_cache;
static HashMap<String, OwnPtr<TimeZoneData>> s_time_zone_cache;
Optional<LocaleData&> LocaleData::for_locale(StringView locale)
@ -114,6 +116,53 @@ icu::TimeZoneNames& LocaleData::time_zone_names()
return *m_time_zone_names;
}
Optional<CalendarData&> CalendarData::for_calendar(String const& calendar)
{
auto& calendar_data = s_calendar_cache.ensure(calendar, [&]() -> OwnPtr<CalendarData> {
UErrorCode status = U_ZERO_ERROR;
auto const* legacy_calendar = uloc_toLegacyType("calendar", ByteString(calendar).characters());
if (!legacy_calendar)
return nullptr;
auto locale_data = LocaleData::for_locale("und"sv);
VERIFY(locale_data.has_value());
locale_data->locale().setKeywordValue("calendar", legacy_calendar, status);
if (icu_failure(status))
return nullptr;
auto icu_calendar = adopt_own_if_nonnull(icu::Calendar::createInstance(locale_data->locale(), status));
if (icu_failure(status))
return nullptr;
return adopt_own(*new CalendarData { icu_calendar.release_nonnull() });
});
if (calendar_data)
return *calendar_data;
return {};
}
void CalendarData::adjust_time_range_for_proleptic_calendar(icu::Calendar& icu_calendar)
{
// https://tc39.es/ecma262/#sec-time-values-and-time-range
// A time value supports a slightly smaller range of -8,640,000,000,000,000 to 8,640,000,000,000,000 milliseconds.
static constexpr UDate ECMA_262_MINIMUM_TIME = -8.64E15;
UErrorCode status = U_ZERO_ERROR;
if (auto* gregorian_calendar = as_if<icu::GregorianCalendar>(icu_calendar)) {
gregorian_calendar->setGregorianChange(ECMA_262_MINIMUM_TIME, status);
verify_icu_success(status);
}
}
CalendarData::CalendarData(NonnullOwnPtr<icu::Calendar> calendar)
: m_calendar(move(calendar))
{
adjust_time_range_for_proleptic_calendar(*m_calendar);
}
Optional<TimeZoneData&> TimeZoneData::for_time_zone(StringView time_zone)
{
auto time_zone_data = s_time_zone_cache.get(time_zone);

View file

@ -25,6 +25,7 @@
#include <unicode/uversion.h>
U_NAMESPACE_BEGIN
class Calendar;
class DateTimePatternGenerator;
class LocaleDisplayNames;
class NumberingSystem;
@ -68,6 +69,20 @@ private:
Optional<DigitalFormat> m_digital_format;
};
class CalendarData {
public:
static Optional<CalendarData&> for_calendar(String const& calendar);
static void adjust_time_range_for_proleptic_calendar(icu::Calendar&);
ALWAYS_INLINE icu::Calendar& calendar() { return *m_calendar; }
private:
explicit CalendarData(NonnullOwnPtr<icu::Calendar>);
NonnullOwnPtr<icu::Calendar> m_calendar;
};
class TimeZoneData {
public:
static Optional<TimeZoneData&> for_time_zone(StringView time_zone);