diff --git a/libgnucash/engine/gnc-datetime.cpp b/libgnucash/engine/gnc-datetime.cpp index a0f09ac8270..9a82d63fef2 100644 --- a/libgnucash/engine/gnc-datetime.cpp +++ b/libgnucash/engine/gnc-datetime.cpp @@ -82,54 +82,7 @@ static constexpr auto ticks_per_second = INT64_C(1000000); static constexpr auto ticks_per_second = INT64_C(1000000000); #endif - -static icu::Locale -get_LCTIME_locale() -{ - const char* lc_time_locale = setlocale(LC_TIME, nullptr); - - if (lc_time_locale == nullptr) - return icu::Locale::getDefault(); - - std::string localeStr(lc_time_locale); - size_t dotPos = localeStr.find('.'); - if (dotPos != std::string::npos) - localeStr = localeStr.substr(0, dotPos); - - return icu::Locale::createCanonical (localeStr.c_str()); -} - -static Date -icu_string_to_date (const std::string str) -{ - UErrorCode status = U_ZERO_ERROR; - icu::Locale locale = get_LCTIME_locale(); - - static std::unique_ptr formatter(icu::DateFormat::createDateInstance(icu::DateFormat::kDefault, locale)); - if (formatter == nullptr) - throw std::invalid_argument ("Cannot create date formatter."); - - static std::unique_ptr calendar(icu::Calendar::createInstance(locale, status)); - if (U_FAILURE(status)) - throw std::invalid_argument("Cannot create calendar instance."); - calendar->setLenient (false); - - icu::UnicodeString input = icu::UnicodeString::fromUTF8(str); - icu::ParsePosition parsePos; - - UDate date = formatter->parse(input, parsePos); - if (parsePos.getErrorIndex() != -1 || parsePos.getIndex() != input.length()) - throw std::invalid_argument ("Cannot parse string"); - - calendar->setTime(date, status); - if (U_FAILURE(status)) - throw std::invalid_argument ("Cannot set calendar time"); - - return Date (calendar->get(UCAL_YEAR, status), - calendar->get(UCAL_MONTH, status) + 1, - calendar->get(UCAL_DATE, status)); -} - +boost::gregorian::date static gregorian_date_from_locale_string (const std::string& str); /* Vector of date formats understood by gnucash and corresponding @@ -142,7 +95,7 @@ const std::vector GncDate::c_formats ({ GncDateFormat { N_("UK date"), boost::gregorian::from_uk_string }, GncDateFormat { N_("US date"), boost::gregorian::from_us_string }, GncDateFormat { N_("ISO date"), boost::gregorian::from_string }, - GncDateFormat { N_("Locale"), icu_string_to_date }, + GncDateFormat { N_("Locale"), gregorian_date_from_locale_string }, GncDateFormat { N_("y-m-d"), "(?:" // either y-m-d @@ -666,6 +619,72 @@ GncDateTimeImpl::timestamp() return str.substr(0, 8) + str.substr(9, 15); } + + + +struct ICUResources +{ + std::unique_ptr formatter; + std::unique_ptr calendar; +}; + +static ICUResources& +get_icu_resources() +{ + static ICUResources rv; + + if (!rv.formatter) + { + icu::Locale locale; + + const char* lc_time_locale = setlocale(LC_TIME, nullptr); + if (lc_time_locale != nullptr) + { + std::string localeStr(lc_time_locale); + size_t dotPos = localeStr.find('.'); + if (dotPos != std::string::npos) + localeStr = localeStr.substr(0, dotPos); + + locale = icu::Locale::createCanonical (localeStr.c_str()); + } + + rv.formatter.reset(icu::DateFormat::createDateInstance(icu::DateFormat::kDefault, locale)); + if (!rv.formatter) + throw std::invalid_argument("Cannot create date formatter."); + + UErrorCode status = U_ZERO_ERROR; + rv.calendar.reset(icu::Calendar::createInstance(locale, status)); + if (U_FAILURE(status)) + throw std::invalid_argument("Cannot create calendar instance."); + + rv.calendar->setLenient(false); + } + + return rv; +} + +boost::gregorian::date +static gregorian_date_from_locale_string (const std::string& str) +{ + ICUResources& resources = get_icu_resources(); + + icu::UnicodeString input = icu::UnicodeString::fromUTF8(str); + icu::ParsePosition parsePos; + + UDate date = resources.formatter->parse(input, parsePos); + if (parsePos.getErrorIndex() != -1 || parsePos.getIndex() != input.length()) + throw std::invalid_argument ("Cannot parse string"); + + UErrorCode status = U_ZERO_ERROR; + resources.calendar->setTime(date, status); + if (U_FAILURE(status)) + throw std::invalid_argument ("Cannot set calendar time"); + + return boost::gregorian::date (resources.calendar->get(UCAL_YEAR, status), + resources.calendar->get(UCAL_MONTH, status) + 1, + resources.calendar->get(UCAL_DATE, status)); +} + /* Member function definitions for GncDateImpl. */ GncDateImpl::GncDateImpl(const std::string str, const std::string fmt) :