From 8c4ad1d6ab4b15a1b5ee6d537c50ba2b7a113f6c Mon Sep 17 00:00:00 2001 From: Christopher Lam Date: Sun, 1 Sep 2024 16:08:55 +0800 Subject: [PATCH] GncDateFormat -> locales part2: save/restore: date-format -> locales --- .../csv-imp/assistant-csv-price-import.cpp | 19 ++- .../csv-imp/assistant-csv-trans-import.cpp | 20 ++- .../csv-imp/gnc-imp-props-price.cpp | 2 +- .../csv-imp/gnc-imp-props-price.hpp | 6 +- .../csv-imp/gnc-imp-props-tx.cpp | 8 +- .../csv-imp/gnc-imp-props-tx.hpp | 14 +- .../csv-imp/gnc-imp-settings-csv.cpp | 10 +- .../csv-imp/gnc-imp-settings-csv.hpp | 4 +- .../csv-imp/gnc-import-price.cpp | 10 +- .../csv-imp/gnc-import-price.hpp | 4 +- .../import-export/csv-imp/gnc-import-tx.cpp | 14 +- .../import-export/csv-imp/gnc-import-tx.hpp | 4 +- libgnucash/core-utils/gnc-locale-utils.cpp | 12 ++ libgnucash/core-utils/gnc-locale-utils.hpp | 3 + libgnucash/engine/gnc-datetime.cpp | 144 +++++------------- libgnucash/engine/gnc-datetime.hpp | 50 +----- 16 files changed, 120 insertions(+), 204 deletions(-) diff --git a/gnucash/import-export/csv-imp/assistant-csv-price-import.cpp b/gnucash/import-export/csv-imp/assistant-csv-price-import.cpp index ddb0a2a1e40..59308bf800e 100644 --- a/gnucash/import-export/csv-imp/assistant-csv-price-import.cpp +++ b/gnucash/import-export/csv-imp/assistant-csv-price-import.cpp @@ -35,6 +35,7 @@ #include #include +#include "gnc-locale-utils.hpp" #include "gnc-ui.h" #include "gnc-uri-utils.h" #include "gnc-ui-util.h" @@ -659,8 +660,8 @@ CsvImpPriceAssist::CsvImpPriceAssist () /* Add in the date format combo box and hook it up to an event handler. */ date_format_combo = GTK_COMBO_BOX_TEXT(gtk_combo_box_text_new()); - for (auto& date_fmt : GncDate::c_formats) - gtk_combo_box_text_append_text (date_format_combo, _(date_fmt.m_fmt.c_str())); + for (auto locale : gnc_get_available_locales()) + gtk_combo_box_text_append_text (date_format_combo, _(locale.c_str())); gtk_combo_box_set_active (GTK_COMBO_BOX(date_format_combo), 0); g_signal_connect (G_OBJECT(date_format_combo), "changed", G_CALLBACK(csv_price_imp_preview_date_fmt_sel_cb), this); @@ -1159,7 +1160,11 @@ CsvImpPriceAssist::preview_update_encoding (const char* encoding) void CsvImpPriceAssist::preview_update_date_format () { - price_imp->date_format (gtk_combo_box_get_active (GTK_COMBO_BOX(date_format_combo))); + if (char *text = gtk_combo_box_text_get_active_text(date_format_combo)) + { + price_imp->date_locale (text); + g_free (text); + } preview_refresh_table (); } @@ -1764,8 +1769,12 @@ CsvImpPriceAssist::preview_refresh () (price_imp->file_format() != GncImpFileFormat::CSV)); // This section deals with the combo's and character encoding - gtk_combo_box_set_active (GTK_COMBO_BOX(date_format_combo), - price_imp->date_format()); + auto locales = gnc_get_available_locales(); + auto locale_it = std::find (locales.begin(), locales.end(), price_imp->date_locale()); + if (locale_it != locales.end()) + gtk_combo_box_set_active (GTK_COMBO_BOX(date_format_combo), + std::distance (locales.begin(), locale_it)); + gtk_combo_box_set_active (GTK_COMBO_BOX(currency_format_combo), price_imp->currency_format()); go_charmap_sel_set_encoding (encselector, price_imp->encoding().c_str()); diff --git a/gnucash/import-export/csv-imp/assistant-csv-trans-import.cpp b/gnucash/import-export/csv-imp/assistant-csv-trans-import.cpp index d1cb2480eaa..b441dda9cfa 100644 --- a/gnucash/import-export/csv-imp/assistant-csv-trans-import.cpp +++ b/gnucash/import-export/csv-imp/assistant-csv-trans-import.cpp @@ -43,6 +43,7 @@ #include "gnc-ui-util.h" #include "dialog-utils.h" +#include "gnc-locale-utils.hpp" #include "gnc-component-manager.h" #include "gnc-state.h" @@ -602,8 +603,9 @@ CsvImpTransAssist::CsvImpTransAssist () /* Add in the date format combo box and hook it up to an event handler. */ date_format_combo = GTK_COMBO_BOX_TEXT(gtk_combo_box_text_new()); - for (auto& date_fmt : GncDate::c_formats) - gtk_combo_box_text_append_text (date_format_combo, _(date_fmt.m_fmt.c_str())); + for (auto locale : gnc_get_available_locales()) + gtk_combo_box_text_append_text (date_format_combo, _(locale.c_str())); + gtk_combo_box_set_active (GTK_COMBO_BOX(date_format_combo), 0); g_signal_connect (G_OBJECT(date_format_combo), "changed", G_CALLBACK(csv_tximp_preview_date_fmt_sel_cb), this); @@ -1137,7 +1139,11 @@ CsvImpTransAssist::preview_update_encoding (const char* encoding) void CsvImpTransAssist::preview_update_date_format () { - tx_imp->date_format (gtk_combo_box_get_active (GTK_COMBO_BOX(date_format_combo))); + if (char *text = gtk_combo_box_text_get_active_text(date_format_combo)) + { + tx_imp->date_locale (text); + g_free (text); + } preview_refresh_table (); } @@ -1690,8 +1696,12 @@ CsvImpTransAssist::preview_refresh () (tx_imp->file_format() != GncImpFileFormat::CSV)); // Set Date & Currency Format and Character encoding - gtk_combo_box_set_active (GTK_COMBO_BOX(date_format_combo), - tx_imp->date_format()); + auto locales = gnc_get_available_locales(); + auto locale_it = std::find (locales.begin(), locales.end(), tx_imp->date_locale()); + if (locale_it != locales.end()) + gtk_combo_box_set_active (GTK_COMBO_BOX(date_format_combo), + std::distance (locales.begin(), locale_it)); + gtk_combo_box_set_active (GTK_COMBO_BOX(currency_format_combo), tx_imp->currency_format()); go_charmap_sel_set_encoding (encselector, tx_imp->encoding().c_str()); diff --git a/gnucash/import-export/csv-imp/gnc-imp-props-price.cpp b/gnucash/import-export/csv-imp/gnc-imp-props-price.cpp index 666b4098ea3..edac5c5b988 100644 --- a/gnucash/import-export/csv-imp/gnc-imp-props-price.cpp +++ b/gnucash/import-export/csv-imp/gnc-imp-props-price.cpp @@ -162,7 +162,7 @@ void GncImportPrice::set (GncPricePropType prop_type, const std::string& value, { case GncPricePropType::DATE: m_date.reset(); - m_date = GncDate(value, GncDate::c_formats[m_date_format].m_fmt); // Throws if parsing fails + m_date = GncDate(value, m_date_locale); // Throws if parsing fails break; case GncPricePropType::AMOUNT: diff --git a/gnucash/import-export/csv-imp/gnc-imp-props-price.hpp b/gnucash/import-export/csv-imp/gnc-imp-props-price.hpp index fb35cb39688..4e25409785e 100644 --- a/gnucash/import-export/csv-imp/gnc-imp-props-price.hpp +++ b/gnucash/import-export/csv-imp/gnc-imp-props-price.hpp @@ -83,11 +83,11 @@ GncNumeric parse_amount_price (const std::string &str, int currency_format); struct GncImportPrice { public: - GncImportPrice (int date_format, int currency_format) : m_date_format{date_format}, + GncImportPrice (std::string date_locale, int currency_format) : m_date_locale{date_locale}, m_currency_format{currency_format}{}; void set (GncPricePropType prop_type, const std::string& value, bool enable_test_empty); - void set_date_format (int date_format) { m_date_format = date_format ;} + void set_date_locale (std::string date_locale) { m_date_locale = date_locale ;} void set_currency_format (int currency_format) { m_currency_format = currency_format ;} void reset (GncPricePropType prop_type); std::string verify_essentials (void); @@ -102,7 +102,7 @@ struct GncImportPrice std::string errors(); private: - int m_date_format; + std::string m_date_locale; int m_currency_format; std::optional m_date; std::optional m_amount; diff --git a/gnucash/import-export/csv-imp/gnc-imp-props-tx.cpp b/gnucash/import-export/csv-imp/gnc-imp-props-tx.cpp index 49ab27ab358..2ab08ad24d3 100644 --- a/gnucash/import-export/csv-imp/gnc-imp-props-tx.cpp +++ b/gnucash/import-export/csv-imp/gnc-imp-props-tx.cpp @@ -239,7 +239,7 @@ void GncPreTrans::set (GncTransPropType prop_type, const std::string& value) case GncTransPropType::DATE: m_date.reset(); if (!value.empty()) - m_date = GncDate(value, GncDate::c_formats[m_date_format].m_fmt); // Throws if parsing fails + m_date = GncDate(value, m_date_locale); else if (!m_multi_split) throw std::invalid_argument ( (bl::format (std::string{_("Date field can not be empty if 'Multi-split' option is unset.\n")}) % @@ -527,15 +527,13 @@ void GncPreSplit::set (GncTransPropType prop_type, const std::string& value) case GncTransPropType::REC_DATE: m_rec_date.reset(); if (!value.empty()) - m_rec_date = GncDate (value, - GncDate::c_formats[m_date_format].m_fmt); // Throws if parsing fails + m_rec_date = GncDate (value, m_date_locale); // Throws if parsing fails break; case GncTransPropType::TREC_DATE: m_trec_date.reset(); if (!value.empty()) - m_trec_date = GncDate (value, - GncDate::c_formats[m_date_format].m_fmt); // Throws if parsing fails + m_trec_date = GncDate (value, m_date_locale); // Throws if parsing fails break; default: diff --git a/gnucash/import-export/csv-imp/gnc-imp-props-tx.hpp b/gnucash/import-export/csv-imp/gnc-imp-props-tx.hpp index 967950e755c..78c4dbc6fee 100644 --- a/gnucash/import-export/csv-imp/gnc-imp-props-tx.hpp +++ b/gnucash/import-export/csv-imp/gnc-imp-props-tx.hpp @@ -154,11 +154,11 @@ struct DraftTransaction class GncPreTrans { public: - GncPreTrans(int date_format, bool multi_split) - : m_date_format{date_format}, m_multi_split{multi_split}, m_currency{nullptr} {}; + GncPreTrans(const std::string date_locale, bool multi_split) + : m_date_locale{date_locale}, m_multi_split{multi_split}, m_currency{nullptr} {}; void set (GncTransPropType prop_type, const std::string& value); - void set_date_format (int date_format) { m_date_format = date_format ;} + void set_date_locale (const std::string date_locale) { m_date_locale = date_locale ;} void set_multi_split (bool multi_split) { m_multi_split = multi_split ;} void reset (GncTransPropType prop_type); StrVec verify_essentials (void); @@ -190,7 +190,7 @@ class GncPreTrans private: - int m_date_format; + std::string m_date_locale; bool m_multi_split; std::optional m_differ; std::optional m_date; @@ -221,12 +221,12 @@ class GncPreTrans class GncPreSplit { public: - GncPreSplit (int date_format, int currency_format) : m_date_format{date_format}, + GncPreSplit (const std::string date_locale, int currency_format) : m_date_locale{date_locale}, m_currency_format{currency_format} {}; void set (GncTransPropType prop_type, const std::string& value); void reset (GncTransPropType prop_type); void add (GncTransPropType prop_type, const std::string& value); - void set_date_format (int date_format) { m_date_format = date_format ;} + void set_date_locale (const std::string date_locale) { m_date_locale = date_locale ;} void set_currency_format (int currency_format) { m_currency_format = currency_format; } void set_pre_trans (std::shared_ptr pre_trans) { m_pre_trans = pre_trans; } std::shared_ptr get_pre_trans (void) { return m_pre_trans; } @@ -241,7 +241,7 @@ class GncPreSplit void UpdateCrossSplitCounters (); std::shared_ptr m_pre_trans; - int m_date_format; + std::string m_date_locale; int m_currency_format; std::optional m_action; std::optional m_account; diff --git a/gnucash/import-export/csv-imp/gnc-imp-settings-csv.cpp b/gnucash/import-export/csv-imp/gnc-imp-settings-csv.cpp index ae5e6f396f1..e04a0d9e6a2 100644 --- a/gnucash/import-export/csv-imp/gnc-imp-settings-csv.cpp +++ b/gnucash/import-export/csv-imp/gnc-imp-settings-csv.cpp @@ -34,6 +34,7 @@ #include #include +#include "gnc-locale-utils.hpp" #include "Account.h" #include "gnc-state.h" #include "gnc-ui-util.h" @@ -150,7 +151,7 @@ CsvImportSettings::load (void) if (key_char) g_free (key_char); - m_date_format = g_key_file_get_integer (keyfile, group.c_str(), CSV_DATE, &key_error); + m_date_locale = g_key_file_get_string (keyfile, group.c_str(), CSV_DATE, &key_error); m_load_error |= handle_load_error (&key_error, group); m_currency_format = g_key_file_get_integer (keyfile, group.c_str(), CSV_CURRENCY, &key_error); @@ -203,13 +204,12 @@ CsvImportSettings::save (void) (m_file_format == GncImpFileFormat::CSV) ? true : false); g_key_file_set_string (keyfile, group.c_str(), CSV_SEP, m_separators.c_str()); - g_key_file_set_integer (keyfile, group.c_str(), CSV_DATE, m_date_format); + g_key_file_set_string (keyfile, group.c_str(), CSV_DATE, m_date_locale.c_str()); std::ostringstream cmt_ss; cmt_ss << "Supported date formats: "; int fmt_num = 0; - std::for_each (GncDate::c_formats.cbegin(), GncDate::c_formats.cend(), - [&cmt_ss, &fmt_num](const GncDateFormat& fmt) - { cmt_ss << fmt_num++ << ": '" << fmt.m_fmt << "', "; }); + for (auto loc : gnc_get_available_locales()) + cmt_ss << fmt_num++ << ": '" << loc << "', "; auto cmt = cmt_ss.str().substr(0, static_cast(cmt_ss.tellp()) - 2); g_key_file_set_comment (keyfile, group.c_str(), CSV_DATE, cmt.c_str(), nullptr); g_key_file_set_integer (keyfile, group.c_str(), CSV_CURRENCY, m_currency_format); diff --git a/gnucash/import-export/csv-imp/gnc-imp-settings-csv.hpp b/gnucash/import-export/csv-imp/gnc-imp-settings-csv.hpp index 39b48e65ff8..c2f532fe5bb 100644 --- a/gnucash/import-export/csv-imp/gnc-imp-settings-csv.hpp +++ b/gnucash/import-export/csv-imp/gnc-imp-settings-csv.hpp @@ -50,7 +50,7 @@ enum SETTINGS_COL {SET_GROUP, SET_NAME}; struct CsvImportSettings { CsvImportSettings() : m_file_format (GncImpFileFormat::CSV), m_encoding {"UTF-8"}, - m_date_format {0}, m_currency_format {0}, + m_date_locale {"en_AU"}, m_currency_format {0}, m_skip_start_lines{0}, m_skip_end_lines{0}, m_skip_alt_lines (false), m_separators {","}, m_load_error {false} { } virtual ~CsvImportSettings() = default; @@ -75,7 +75,7 @@ void remove (void); std::string m_name; // Name given to this preset by the user GncImpFileFormat m_file_format; // CSV import Format std::string m_encoding; // File encoding -int m_date_format; // Date Active id +std::string m_date_locale; // Date Active id int m_currency_format; // Currency Active id uint32_t m_skip_start_lines; // Number of header rows to skip uint32_t m_skip_end_lines; // Number of footer rows to skip diff --git a/gnucash/import-export/csv-imp/gnc-import-price.cpp b/gnucash/import-export/csv-imp/gnc-import-price.cpp index ee3cb0b7140..98fc1d9d63f 100644 --- a/gnucash/import-export/csv-imp/gnc-import-price.cpp +++ b/gnucash/import-export/csv-imp/gnc-import-price.cpp @@ -213,15 +213,15 @@ void GncPriceImport::currency_format (int currency_format) } int GncPriceImport::currency_format () { return m_settings.m_currency_format; } -void GncPriceImport::date_format (int date_format) +void GncPriceImport::date_locale (std::string date_locale) { - m_settings.m_date_format = date_format; + m_settings.m_date_locale = date_locale; /* Reparse all date related columns */ std::vector dates = { GncPricePropType::DATE }; reset_formatted_column (dates); } -int GncPriceImport::date_format () { return m_settings.m_date_format; } +std::string GncPriceImport::date_locale () { return m_settings.m_date_locale; } /** Converts raw file data using a new encoding. This function must be * called after load_file only if load_file guessed @@ -385,7 +385,7 @@ void GncPriceImport::tokenize (bool guessColTypes) auto length = tokenized_line.size(); if (length > 0) m_parsed_lines.push_back (std::make_tuple (tokenized_line, std::string(), - std::make_shared(date_format(), currency_format()), + std::make_shared(date_locale(), currency_format()), false)); if (length > max_cols) max_cols = length; @@ -750,7 +750,7 @@ GncPriceImport::set_column_type_price (uint32_t position, GncPricePropType type, /* Reset date and currency formats for each price props object * to ensure column updates use the most recent one */ - std::get(*parsed_lines_it)->set_date_format (m_settings.m_date_format); + std::get(*parsed_lines_it)->set_date_locale (m_settings.m_date_locale); std::get(*parsed_lines_it)->set_currency_format (m_settings.m_currency_format); uint32_t row = parsed_lines_it - m_parsed_lines.begin(); diff --git a/gnucash/import-export/csv-imp/gnc-import-price.hpp b/gnucash/import-export/csv-imp/gnc-import-price.hpp index 4ada98ca9b0..0f705a3074f 100644 --- a/gnucash/import-export/csv-imp/gnc-import-price.hpp +++ b/gnucash/import-export/csv-imp/gnc-import-price.hpp @@ -100,8 +100,8 @@ class GncPriceImport void currency_format (int currency_format); int currency_format (); - void date_format (int date_format); - int date_format (); + void date_locale (std::string date_locale); + std::string date_locale (); void encoding (const std::string& encoding); std::string encoding (); diff --git a/gnucash/import-export/csv-imp/gnc-import-tx.cpp b/gnucash/import-export/csv-imp/gnc-import-tx.cpp index 6c2f2837792..242f1cf3b8d 100644 --- a/gnucash/import-export/csv-imp/gnc-import-tx.cpp +++ b/gnucash/import-export/csv-imp/gnc-import-tx.cpp @@ -228,9 +228,9 @@ void GncTxImport::currency_format (int currency_format) } int GncTxImport::currency_format () { return m_settings.m_currency_format; } -void GncTxImport::date_format (int date_format) +void GncTxImport::date_locale (std::string date_locale) { - m_settings.m_date_format = date_format; + m_settings.m_date_locale = date_locale; /* Reparse all date related columns */ std::vector dates = { GncTransPropType::DATE, @@ -238,7 +238,7 @@ void GncTxImport::date_format (int date_format) GncTransPropType::TREC_DATE}; reset_formatted_column (dates); } -int GncTxImport::date_format () { return m_settings.m_date_format; } +std::string GncTxImport::date_locale () { return m_settings.m_date_locale; } /** Converts raw file data using a new encoding. This function must be * called after load_file only if load_file guessed @@ -407,8 +407,8 @@ void GncTxImport::tokenize (bool guessColTypes) auto length = tokenized_line.size(); if (length > 0) { - auto pretrans = std::make_shared(date_format(), m_settings.m_multi_split); - auto presplit = std::make_shared(date_format(), currency_format()); + auto pretrans = std::make_shared(date_locale(), m_settings.m_multi_split); + auto presplit = std::make_shared(date_locale(), currency_format()); presplit->set_pre_trans (std::move (pretrans)); m_parsed_lines.push_back (std::make_tuple (tokenized_line, ErrMap(), presplit->get_pre_trans(), std::move (presplit), false)); @@ -781,7 +781,7 @@ void GncTxImport::update_pre_trans_props (parse_line_t& parsed_line, uint32_t co /* Reset date format for each trans props object * to ensure column updates use the most recent one */ - trans_props->set_date_format (m_settings.m_date_format); + trans_props->set_date_locale (m_settings.m_date_locale); trans_props->set_multi_split (m_settings.m_multi_split); if ((old_type > GncTransPropType::NONE) && (old_type <= GncTransPropType::TRANS_PROPS)) @@ -820,7 +820,7 @@ void GncTxImport::update_pre_split_props (parse_line_t& parsed_line, uint32_t co auto trans_props = std::get (parsed_line); /* Reset date format for each split props object * to ensure column updates use the most recent one */ - split_props->set_date_format (m_settings.m_date_format); + split_props->set_date_locale (m_settings.m_date_locale); if (m_settings.m_multi_split && trans_props->is_part_of( m_parent)) split_props->set_pre_trans (m_parent); else diff --git a/gnucash/import-export/csv-imp/gnc-import-tx.hpp b/gnucash/import-export/csv-imp/gnc-import-tx.hpp index f2bb4e2765e..595a48563b0 100644 --- a/gnucash/import-export/csv-imp/gnc-import-tx.hpp +++ b/gnucash/import-export/csv-imp/gnc-import-tx.hpp @@ -119,8 +119,8 @@ class GncTxImport void currency_format (int currency_format); int currency_format (); - void date_format (int date_format); - int date_format (); + void date_locale (std::string date_locale); + std::string date_locale (); void encoding (const std::string& encoding); std::string encoding (); diff --git a/libgnucash/core-utils/gnc-locale-utils.cpp b/libgnucash/core-utils/gnc-locale-utils.cpp index c73a02dd597..210373ae868 100644 --- a/libgnucash/core-utils/gnc-locale-utils.cpp +++ b/libgnucash/core-utils/gnc-locale-utils.cpp @@ -22,6 +22,7 @@ #include #include #include +#include #include "gnc-locale-utils.hpp" #include @@ -115,3 +116,14 @@ gnc_get_boost_locale() } +std::vector +gnc_get_available_locales () +{ + std::vector rv; + auto num_locales{uloc_countAvailable()}; + rv.reserve (num_locales); + for (int32_t i = 0; i < num_locales; ++i) + if (auto localeID = uloc_getAvailable (i)) + rv.push_back (localeID); + return rv; +} diff --git a/libgnucash/core-utils/gnc-locale-utils.hpp b/libgnucash/core-utils/gnc-locale-utils.hpp index 1473d29a4ff..c33d28971bb 100644 --- a/libgnucash/core-utils/gnc-locale-utils.hpp +++ b/libgnucash/core-utils/gnc-locale-utils.hpp @@ -23,6 +23,7 @@ #define GNC_LOCALE_UTILS_HPP #include +#include #include /** Get the default application locale. @@ -63,4 +64,6 @@ void gnc_init_boost_locale(const std::string& messages_path); */ const std::locale& gnc_get_boost_locale(); +std::vector gnc_get_available_locales (); + #endif /* GNC_LOCALE_UTILS_HPP */ diff --git a/libgnucash/engine/gnc-datetime.cpp b/libgnucash/engine/gnc-datetime.cpp index 308ec24e4ef..f4e59fcf26a 100644 --- a/libgnucash/engine/gnc-datetime.cpp +++ b/libgnucash/engine/gnc-datetime.cpp @@ -45,6 +45,11 @@ #include #include "gnc-timezone.hpp" #include "gnc-datetime.hpp" +#include +#include +#include +#include +#include #define N_(string) string //So that xgettext will find it @@ -77,77 +82,6 @@ static constexpr auto ticks_per_second = INT64_C(1000000); static constexpr auto ticks_per_second = INT64_C(1000000000); #endif -/* Vector of date formats understood by gnucash and corresponding regex - * to parse each from an external source - * Note: while the format names are using a "-" as separator, the - * regexes will accept any of "-/.' " and will also work for dates - * without separators. - */ -const std::vector GncDate::c_formats ({ - GncDateFormat { - N_("y-m-d"), - "(?:" // either y-m-d - "(?[0-9]+)[-/.' ]+" - "(?[0-9]+)[-/.' ]+" - "(?[0-9]+)" - "|" // or CCYYMMDD - "(?[0-9]{4})" - "(?[0-9]{2})" - "(?[0-9]{2})" - ")" - }, - GncDateFormat { - N_("d-m-y"), - "(?:" // either d-m-y - "(?[0-9]+)[-/.' ]+" - "(?[0-9]+)[-/.' ]+" - "(?[0-9]+)" - "|" // or DDMMCCYY - "(?[0-9]{2})" - "(?[0-9]{2})" - "(?[0-9]{4})" - ")" - }, - GncDateFormat { - N_("m-d-y"), - "(?:" // either m-d-y - "(?[0-9]+)[-/.' ]+" - "(?[0-9]+)[-/.' ]+" - "(?[0-9]+)" - "|" // or MMDDCCYY - "(?[0-9]{2})" - "(?[0-9]{2})" - "(?[0-9]{4})" - ")" - }, - // Note year is still checked for in the regexes below - // This is to be able to raise an error if one is found for a yearless date format - GncDateFormat { - (N_("d-m")), - "(?:" // either d-m(-y) - "(?[0-9]+)[-/.' ]+" - "(?[0-9]+)(?:[-/.' ]+" - "(?[0-9]+))?" - "|" // or DDMM(CCYY) - "(?[0-9]{2})" - "(?[0-9]{2})" - "(?[0-9]+)?" - ")" - }, - GncDateFormat { - (N_("m-d")), - "(?:" // either m-d(-y) - "(?[0-9]+)[-/.' ]+" - "(?[0-9]+)(?:[-/.' ]+" - "(?[0-9]+))?" - "|" // or MMDD(CCYY) - "(?[0-9]{2})" - "(?[0-9]{2})" - "(?[0-9]+)?" - ")" - } -}); - /** Private implementation of GncDateTime. See the documentation for that class. */ static LDT @@ -609,42 +543,40 @@ GncDateTimeImpl::timestamp() /* Member function definitions for GncDateImpl. */ -GncDateImpl::GncDateImpl(const std::string str, const std::string fmt) : - m_greg(boost::gregorian::day_clock::local_day()) /* Temporarily initialized to today, will be used and adjusted in the code below */ -{ - auto iter = std::find_if(GncDate::c_formats.cbegin(), GncDate::c_formats.cend(), - [&fmt](const GncDateFormat& v){ return (v.m_fmt == fmt); } ); - if (iter == GncDate::c_formats.cend()) - throw std::invalid_argument(N_("Unknown date format specifier passed as argument.")); - - boost::regex r(iter->m_re); - boost::smatch what; - if(!boost::regex_search(str, what, r)) // regex didn't find a match - throw std::invalid_argument (N_("Value can't be parsed into a date using the selected date format.")); - - // Bail out if a year was found with a yearless format specifier - auto fmt_has_year = (fmt.find('y') != std::string::npos); - if (!fmt_has_year && (what.length("YEAR") != 0)) - throw std::invalid_argument (N_("Value appears to contain a year while the selected format forbids this.")); - - int year; - if (fmt_has_year) - { - /* The input dates have a year, so use that one */ - year = std::stoi (what.str("YEAR")); - - /* We assume two-digit years to be in the range 1969 - 2068. */ - if (year < 69) - year += 2000; - else if (year < 100) - year += 1900; - } - else /* The input dates have no year, so use current year */ - year = m_greg.year(); // Can use m_greg here as it was already initialized in the initializer list earlier +GncDateImpl::GncDateImpl(const std::string str, const std::string locale_str) : + /* Temporarily initialized to today, will be used and adjusted in the code below */ + m_greg(boost::gregorian::day_clock::local_day()) +{ + UErrorCode status = U_ZERO_ERROR; + + icu::Locale locale = icu::Locale::createCanonical (locale_str.c_str()); + std::unique_ptr formatter(icu::DateFormat::createDateInstance(icu::DateFormat::kDefault, locale)); + if (formatter == nullptr) + throw std::invalid_argument ("Cannot parse string"); + + icu::UnicodeString input = icu::UnicodeString::fromUTF8(str); + icu::ParsePosition parsePos; + + UDate date = formatter->parse(input, parsePos); + if (parsePos.getErrorIndex() != -1) + throw std::invalid_argument ("Cannot parse string"); + + std::unique_ptr calendar(icu::Calendar::createInstance(locale, status)); + if (U_FAILURE(status)) + throw std::invalid_argument ("Cannot parse string"); + + calendar->setTime(date, status); + if (U_FAILURE(status)) + throw std::invalid_argument ("Cannot parse string"); + + int32_t day = calendar->get(UCAL_DATE, status); + int32_t month = calendar->get(UCAL_MONTH, status) + 1; + int32_t year = calendar->get(UCAL_YEAR, status); + + if (U_FAILURE(status)) + throw std::invalid_argument ("Cannot parse string"); - m_greg = Date(year, - static_cast(std::stoi (what.str("MONTH"))), - std::stoi (what.str("DAY"))); + m_greg = Date(year, month, day); } gnc_ymd diff --git a/libgnucash/engine/gnc-datetime.hpp b/libgnucash/engine/gnc-datetime.hpp index 77a6039b10a..26f68d7e9f3 100644 --- a/libgnucash/engine/gnc-datetime.hpp +++ b/libgnucash/engine/gnc-datetime.hpp @@ -162,37 +162,6 @@ class GncDateTime std::unique_ptr m_impl; }; -/** GnuCash DateFormat class - * - * A helper class to represent a date format understood - * by the GncDate string/format constructor. Consumers - * of this header file are not supposed to create - * objects of this class themselves. Instead they - * can get a list of the understood formats from the - * GncDate::c_formats class variable and work with those. - */ - -class GncDateFormat -{ -public: - /** Construct a GncDateFormat with a given format and corresponding - * regular expression. This should only be used internally by the - * GncDate implementation. Consumers should never construct a GncDateFormat - * themselves! - */ - GncDateFormat (const char* fmt, const char* re) : - m_fmt(fmt), m_re(re) {} - /** A string representing the format. */ - const std::string m_fmt; -private: - /** Regular expression associated with the format string. This is to and - * only be used internally by the gnc-datetime code. - */ - const std::string m_re; - - friend class GncDateImpl; -}; - /** GnuCash Date class * * The represented date is limited to the period @@ -202,23 +171,6 @@ class GncDateFormat class GncDate { public: - /** A vector with all the date formats supported by the string constructor. - * The currently supported formats are: - * "y-m-d" (including yyyymmdd) - * "d-m-y" (including ddmmyyyy) - * "m-d-y" (including mmddyyyy) - * "d-m" (including ddmm) - * "m-d" (including mmdd) - * - * Notes: - * - while the format names are using a "-" as separator, the - * regexes will accept any of "-/.' " and will also work for dates - * without separators. - * - the format strings are marked for translation so it is possible - * to use a localized version of a format string using gettext. Example: - * gettext(GncDate::c_formats[0]) - */ - static const std::vector c_formats; /** Construct a GncDate representing the current day. */ GncDate(); @@ -252,7 +204,7 @@ class GncDate * (like month being 13, or day being 31 in February) * - fmt doesn't specify a year, yet a year was found in the string */ - GncDate(const std::string str, const std::string fmt); + GncDate(const std::string str, const std::string locale_str); /** Construct a GncDate from a GncDateImpl. */ GncDate(std::unique_ptr impl);