From 8b1b25aee52d3c844a4cbecba7c80b3cd5d8627b Mon Sep 17 00:00:00 2001 From: Bill Nottingham Date: Tue, 22 May 2018 23:37:22 -0400 Subject: [PATCH] Modify date formatters to strip out certian specifiers. strptime/strftime supports various modifiers to their parameters. 'E' and 'O': alternate locale-specific formats (used in default format for Persian, Oriya, Azerbaijani) '-': padding (used in default format for Czech) GnuCash passes dates as integer y/m/d without using locale-specific formats, so we need to strip out 'E' and 'O' from the format when scanning dates or determining separators in gnc-date. None of '-', 'E', or 'O' are supported by boost (and '-' causes errors), so strip them out from formatters in gnc-datetime as well. See https://bugzilla.gnome.org/show_bug.cgi?id=795247. --- libgnucash/engine/gnc-date.cpp | 55 ++++++++++++++++++------------ libgnucash/engine/gnc-datetime.cpp | 36 +++++++++++++++++-- 2 files changed, 66 insertions(+), 25 deletions(-) diff --git a/libgnucash/engine/gnc-date.cpp b/libgnucash/engine/gnc-date.cpp index 881c090fef..d82a2aeaf6 100644 --- a/libgnucash/engine/gnc-date.cpp +++ b/libgnucash/engine/gnc-date.cpp @@ -728,6 +728,32 @@ floordiv(int a, int b) } } +/* Normalize the localized date format to avoid date scanning issues. + * + * The 'O' and 'E' format modifiers are for localized input/output + * characters. Remove them as we are always using Arabic numbers. + */ +static char * +normalize_format (const char *format) +{ + gint counter = 0, n_counter = 0; + gchar *normalized; + + normalized = g_strdup(format); + while (format[counter] != '\0') + { + normalized[n_counter] = format[counter]; + if ((format[counter] == '%') && \ + (format[counter+1] == 'E' || format[counter+1] == 'O')) + counter++; // skip format modifier + + counter++; + n_counter++; + } + normalized[n_counter] = '\0'; + return normalized; +} + /* Convert a string into day, month and year integers Convert a string into day / month / year integers according to @@ -820,30 +846,12 @@ qof_scan_date_internal (const char *buff, int *day, int *month, int *year, if (buff[0] != '\0') { struct tm thetime; - gchar *format = g_strdup (GNC_D_FMT); - gchar *stripped_format = g_strdup (GNC_D_FMT); - gint counter = 0, stripped_counter = 0; - - /* strptime can't handle the - format modifier - * let's strip it out of the format before using it - */ - while (format[counter] != '\0') - { - stripped_format[stripped_counter] = format[counter]; - if ((format[counter] == '%') && (format[counter+1] == '-')) - counter++; // skip - format modifier - - counter++; - stripped_counter++; - } - stripped_format[stripped_counter] = '\0'; - g_free (format); - + gchar *normalized_format = normalize_format(GNC_D_FMT); /* Parse time string. */ memset(&thetime, -1, sizeof(struct tm)); - strptime (buff, stripped_format, &thetime); - g_free (stripped_format); + strptime (buff, normalized_format, &thetime); + g_free (normalized_format); if (third_field) { @@ -1037,10 +1045,13 @@ char dateSeparator (void) struct tm tm; time64 secs; gchar *s; + gchar *normalized_fmt; secs = gnc_time (NULL); gnc_localtime_r(&secs, &tm); - qof_strftime(string, sizeof(string), GNC_D_FMT, &tm); + normalized_fmt = normalize_format(qof_date_format_get_string(dateFormat)); + qof_strftime(string, sizeof(string), normalized_fmt, &tm); + g_free(normalized_fmt); for (s = string; *s != '\0'; s++) if (!isdigit(*s)) diff --git a/libgnucash/engine/gnc-datetime.cpp b/libgnucash/engine/gnc-datetime.cpp index d0958de356..344fe5cf44 100644 --- a/libgnucash/engine/gnc-datetime.cpp +++ b/libgnucash/engine/gnc-datetime.cpp @@ -289,6 +289,30 @@ tz_from_string(std::string str) return TZ_Ptr(new PTZ(tzstr)); } +/* The 'O', 'E', and '-' format modifiers are not supported by + * boost's output facets. Remove them. + */ +static char * +normalize_format (const char *format) +{ + int counter = 0, n_counter = 0; + char *normalized; + + normalized = strdup(format); + while (format[counter] != '\0') + { + normalized[n_counter] = format[counter]; + if ((format[counter] == '%') && \ + (format[counter+1] == '-' || format[counter+1] == 'E' || format[counter+1] == 'O')) + counter++; // skip format modifier + + counter++; + n_counter++; + } + normalized[n_counter] = '\0'; + return normalized; +} + GncDateTimeImpl::GncDateTimeImpl(std::string str) : m_time(unix_epoch, utc_zone) { @@ -371,9 +395,11 @@ GncDateTimeImpl::format(const char* format) const { using Facet = boost::local_time::local_time_facet; std::stringstream ss; + char *normalized_format = normalize_format(format); //The stream destructor frees the facet, so it must be heap-allocated. - auto output_facet(new Facet(format)); + auto output_facet(new Facet(normalized_format)); ss.imbue(std::locale(std::locale(), output_facet)); + free(normalized_format); ss << m_time; return ss.str(); } @@ -383,9 +409,11 @@ GncDateTimeImpl::format_zulu(const char* format) const { using Facet = boost::posix_time::time_facet; std::stringstream ss; + char *normalized_format = normalize_format(format); //The stream destructor frees the facet, so it must be heap-allocated. - auto output_facet(new Facet(format)); + auto output_facet(new Facet(normalized_format)); ss.imbue(std::locale(std::locale(), output_facet)); + free(normalized_format); ss << m_time.utc_time(); return ss.str(); } @@ -442,9 +470,11 @@ GncDateImpl::format(const char* format) const { using Facet = boost::gregorian::date_facet; std::stringstream ss; + char *normalized_format = normalize_format(format); //The stream destructor frees the facet, so it must be heap-allocated. - auto output_facet(new Facet(format)); + auto output_facet(new Facet(normalized_format)); ss.imbue(std::locale(std::locale(), output_facet)); + free(normalized_format); ss << m_greg; return ss.str(); }