Skip to content

Commit

Permalink
move locale string->date to gnc-locale-utils.cpp
Browse files Browse the repository at this point in the history
  • Loading branch information
christopherlam committed Sep 12, 2024
1 parent 941c365 commit b5c4333
Show file tree
Hide file tree
Showing 3 changed files with 72 additions and 55 deletions.
67 changes: 67 additions & 0 deletions libgnucash/core-utils/gnc-locale-utils.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,11 @@
#include <boost/locale.hpp>
#include "gnc-locale-utils.hpp"
#include <config.h>
#include <unicode/smpdtfmt.h>
#include <unicode/locid.h>
#include <unicode/udat.h>
#include <unicode/parsepos.h>
#include <unicode/calendar.h>

/** Cache the UI locale
*
Expand Down Expand Up @@ -115,3 +120,65 @@ gnc_get_boost_locale()
}


struct ICUResources
{
std::unique_ptr<icu::DateFormat> formatter;
std::unique_ptr<icu::Calendar> 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
gnc_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));
}
4 changes: 4 additions & 0 deletions libgnucash/core-utils/gnc-locale-utils.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,8 @@

#include <locale>
#include <string>
#include <boost/date_time/gregorian/gregorian.hpp>


/** Get the default application locale.
*
Expand Down Expand Up @@ -63,4 +65,6 @@ void gnc_init_boost_locale(const std::string& messages_path);
*/
const std::locale& gnc_get_boost_locale();

boost::gregorian::date gnc_gregorian_date_from_locale_string (const std::string& str);

#endif /* GNC_LOCALE_UTILS_HPP */
56 changes: 1 addition & 55 deletions libgnucash/engine/gnc-datetime.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -29,11 +29,6 @@
#include <boost/date_time/local_time/local_time.hpp>
#include <boost/locale.hpp>
#include <boost/regex.hpp>
#include <unicode/smpdtfmt.h>
#include <unicode/locid.h>
#include <unicode/udat.h>
#include <unicode/parsepos.h>
#include <unicode/calendar.h>
#include <libintl.h>
#include <locale.h>
#include <map>
Expand Down Expand Up @@ -83,55 +78,6 @@ 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<icu::DateFormat> formatter(icu::DateFormat::createDateInstance(icu::DateFormat::kDefault, locale));
if (formatter == nullptr)
throw std::invalid_argument ("Cannot create date formatter.");

static std::unique_ptr<icu::Calendar> 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));
}



/* Vector of date formats understood by gnucash and corresponding
* boost string->date or regex to parse each from an external source
* Note: while the format names are using a "-" as separator, the
Expand All @@ -142,7 +88,7 @@ const std::vector<GncDateFormat> 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"), gnc_gregorian_date_from_locale_string },
GncDateFormat {
N_("y-m-d"),
"(?:" // either y-m-d
Expand Down

0 comments on commit b5c4333

Please sign in to comment.