diff options
-rw-r--r-- | src/item.cc | 4 | ||||
-rw-r--r-- | src/item.h | 6 | ||||
-rw-r--r-- | src/session.h | 2 | ||||
-rw-r--r-- | src/system.hh.in | 4 | ||||
-rw-r--r-- | src/textual.cc | 5 | ||||
-rw-r--r-- | src/times.cc | 174 | ||||
-rw-r--r-- | src/times.h | 45 | ||||
-rw-r--r-- | test/regress/461980A1.test | 2 | ||||
-rw-r--r-- | test/regress/55831A79.test | 2 |
9 files changed, 120 insertions, 124 deletions
diff --git a/src/item.cc b/src/item.cc index 9e297052..54f36e11 100644 --- a/src/item.cc +++ b/src/item.cc @@ -114,7 +114,7 @@ void item_t::set_tag(const string& tag, assert(result.second); } -void item_t::parse_tags(const char * p, int current_year) +void item_t::parse_tags(const char * p, optional<date_t::year_type> current_year) { if (const char * b = std::strchr(p, '[')) { if (const char * e = std::strchr(p, ']')) { @@ -160,7 +160,7 @@ void item_t::parse_tags(const char * p, int current_year) } } -void item_t::append_note(const char * p, int current_year) +void item_t::append_note(const char * p, optional<date_t::year_type> current_year) { if (note) { *note += '\n'; @@ -129,8 +129,10 @@ public: virtual void set_tag(const string& tag, const optional<string>& value = none); - virtual void parse_tags(const char * p, int current_year = -1); - virtual void append_note(const char * p, int current_year = -1); + virtual void parse_tags(const char * p, + optional<date_t::year_type> current_year = none); + virtual void append_note(const char * p, + optional<date_t::year_type> current_year = none); static bool use_effective_date; diff --git a/src/session.h b/src/session.h index 5d6a12b9..894c59fa 100644 --- a/src/session.h +++ b/src/session.h @@ -67,7 +67,7 @@ class session_t : public symbol_scope_t public: bool flush_on_next_data_file; - int current_year; + date_t::year_type current_year; shared_ptr<commodity_pool_t> commodity_pool; scoped_ptr<account_t> master; diff --git a/src/system.hh.in b/src/system.hh.in index 2358ddcd..1f4a7d63 100644 --- a/src/system.hh.in +++ b/src/system.hh.in @@ -53,6 +53,7 @@ #include <algorithm> #include <exception> #include <typeinfo> +#include <locale> #include <stdexcept> #include <iostream> #include <streambuf> @@ -98,7 +99,6 @@ typedef std::ostream::pos_type ostream_pos_type; #include <cstdio> #include <cstdlib> #include <cstring> -#include <ctime> #include <csignal> #if defined __FreeBSD__ && __FreeBSD__ <= 4 @@ -144,6 +144,8 @@ typedef std::ostream::pos_type ostream_pos_type; #include <boost/cast.hpp> #include <boost/current_function.hpp> #include <boost/date_time/posix_time/posix_time.hpp> +#include <boost/date_time/posix_time/posix_time_io.hpp> +#include <boost/date_time/gregorian/gregorian_io.hpp> #include <boost/filesystem/convenience.hpp> #include <boost/filesystem/exception.hpp> #include <boost/filesystem/fstream.hpp> diff --git a/src/textual.cc b/src/textual.cc index 35fa0028..967e2f1b 100644 --- a/src/textual.cc +++ b/src/textual.cc @@ -65,7 +65,6 @@ namespace { account_t * master; const path * original_file; accounts_map account_aliases; - int current_year; bool strict; path pathname; @@ -76,6 +75,8 @@ namespace { std::size_t count; std::size_t errors; + optional<date_t::year_type> current_year; + scoped_ptr<auto_xact_finalizer_t> auto_xact_finalizer; instance_t(std::list<account_t *>& _account_stack, @@ -476,7 +477,7 @@ void instance_t::nomarket_directive(char * line) void instance_t::year_directive(char * line) { - current_year = lexical_cast<int>(skip_ws(line + 1)); + current_year = lexical_cast<unsigned short>(skip_ws(line + 1)); } void instance_t::option_directive(char * line) diff --git a/src/times.cc b/src/times.cc index deb51058..45f7ed10 100644 --- a/src/times.cc +++ b/src/times.cc @@ -41,64 +41,83 @@ std::string output_datetime_format = "%Y-%m-%d %H:%M:%S"; std::string output_date_format = "%Y-%m-%d"; namespace { - const char * formats[] = { - "%y/%m/%d", - "%Y/%m/%d", - "%m/%d", - "%Y/%m", - "%y.%m.%d", - "%Y.%m.%d", - "%m.%d", - "%Y.%m", - "%y-%m-%d", - "%Y-%m-%d", - "%m-%d", - "%Y-%m", - NULL + struct date_format_t { + const char * format; + bool has_year; + date_format_t(const char * _format, bool _has_year) + : format(_format), has_year(_has_year) {} }; - bool parse_date_mask(const char * date_str, std::tm& result) - { - if (input_date_format) { - std::memset(&result, -1, sizeof(std::tm)); - if (strptime(date_str, input_date_format->c_str(), &result)) - return true; - } - for (const char ** f = formats; *f; f++) { - std::memset(&result, -1, sizeof(std::tm)); - if (strptime(date_str, *f, &result)) - return true; - } - return false; - } + const date_format_t formats[] = { + date_format_t("%m/%d", false), + date_format_t("%Y/%m/%d", true), + date_format_t("%Y/%m", true), + date_format_t("%y/%m/%d", true), + date_format_t("%m.%d", false), + date_format_t("%Y.%m.%d", true), + date_format_t("%Y.%m", true), + date_format_t("%y.%m.%d", true), + date_format_t("%m-%d", false), + date_format_t("%Y-%m-%d", true), + date_format_t("%Y-%m", true), + date_format_t("%y-%m-%d", true) + }; - bool quick_parse_date(const char * date_str, std::tm& result, const int year) + date_t parse_date_mask_routine(const char * date_str, const date_format_t& df, + optional<date_t::year_type> year, bool& saw_year) { - if (! parse_date_mask(date_str, result)) - return false; + std::string str(date_str); - result.tm_hour = 0; - result.tm_min = 0; - result.tm_sec = 0; + gregorian::date_input_facet * facet(new gregorian::date_input_facet(df.format)); + std::istringstream sstr(str); + sstr.imbue(std::locale(sstr.getloc(), facet)); - if (result.tm_mday == -1) - result.tm_mday = 1; + date_t when; + sstr >> when; - if (result.tm_mon == -1) { - result.tm_mon = 0; + if (! when.is_not_a_date()) { + if (sstr.good() && ! sstr.eof() && sstr.peek() != EOF) + return date_t(); - if (result.tm_mday > (CURRENT_DATE().day() - 1)) - result.tm_mon = 11; + DEBUG("times.parse", "Parsed date string: " << date_str); + DEBUG("times.parse", "Parsed result is: " << when); + DEBUG("times.parse", "Format used was: " << df.format); + + if (! df.has_year) { + saw_year = false; + + when = date_t(year ? *year : CURRENT_DATE().year(), + when.month(), when.day()); + + if (when.month() > CURRENT_DATE().month()) + when -= gregorian::years(1); + } else { + saw_year = true; + } } + return when; + } - if (result.tm_year == -1) { - result.tm_year = (year == -1 ? int(CURRENT_DATE().year()) : year) - 1900; + date_t parse_date_mask(const char * date_str, optional<date_t::year_type> year, + bool& saw_year) + { + if (input_date_format) { + date_format_t df(input_date_format->c_str(), true); + if (! icontains(*input_date_format, "%y")) + df.has_year = false; + date_t when = parse_date_mask_routine(date_str, df, year, saw_year); + if (! when.is_not_a_date()) + return when; + } - if (year == -1 && result.tm_mon > (CURRENT_DATE().month() - 1)) - result.tm_year--; + for (uint8_t i = 0; i < (sizeof(formats) / sizeof(date_format_t)); i++) { + date_t when = parse_date_mask_routine(date_str, formats[i], year, + saw_year); + if (! when.is_not_a_date()) + return when; } - return true; + return date_t(); } } @@ -153,21 +172,24 @@ string_to_month_of_year(const std::string& str) return none; } -datetime_t parse_datetime(const char * str, int) +datetime_t parse_datetime(const char * str, optional<date_t::year_type>) { - std::tm when; - std::memset(&when, -1, sizeof(std::tm)); - if (strptime(str, "%Y/%m/%d %H:%M:%S", &when)) - return posix_time::ptime_from_tm(when); - else - return datetime_t(); + posix_time::time_input_facet * facet + (new posix_time::time_input_facet("%Y/%m/%d %H:%M:%S")); + + std::string temp(str); + std::istringstream sstr(temp); + sstr.imbue(std::locale(sstr.getloc(), facet)); + + datetime_t when; + sstr >> when; + return when; } -date_t parse_date(const char * str, int current_year) +date_t parse_date(const char * str, optional<date_t::year_type> current_year) { - std::tm when; - quick_parse_date(str, when, current_year); - return gregorian::date_from_tm(when); + bool saw_year; + return parse_date_mask(str, current_year, saw_year); } date_t date_interval_t::add_duration(const date_t& date, @@ -436,52 +458,24 @@ namespace { date_t * begin, date_t * end) { - struct std::tm when; + bool saw_year = true; + date_t when = parse_date_mask(word.c_str(), none, saw_year); - if (! parse_date_mask(word.c_str(), when)) + if (when.is_not_a_date()) throw_(date_error, _("Could not parse date mask: %1") << word); - when.tm_hour = 0; - when.tm_min = 0; - when.tm_sec = 0; - when.tm_isdst = -1; - - bool saw_year = true; - bool saw_mon = true; - bool saw_day = true; - - if (when.tm_year == -1) { - when.tm_year = CURRENT_DATE().year() - 1900; - saw_year = false; - } - if (when.tm_mon == -1) { - when.tm_mon = 0; - saw_mon = false; - } else { - saw_year = false; // don't increment by year if month used - } - if (when.tm_mday == -1) { - when.tm_mday = 1; - saw_day = false; - } else { - saw_mon = false; // don't increment by month if day used - saw_year = false; // don't increment by year if day used - } - if (begin) { - *begin = gregorian::date_from_tm(when); + *begin = when; if (end) { if (saw_year) *end = *begin + gregorian::years(1); - else if (saw_mon) + else *end = *begin + gregorian::months(1); - else if (saw_day) - *end = *begin + gregorian::days(1); } } else if (end) { - *end = gregorian::date_from_tm(when); + *end = when; } } diff --git a/src/times.h b/src/times.h index 1ff98325..247c9393 100644 --- a/src/times.h +++ b/src/times.h @@ -81,39 +81,34 @@ string_to_day_of_week(const std::string& str); optional<date_time::months_of_year> string_to_month_of_year(const std::string& str); -datetime_t parse_datetime(const char * str, int current_year = -1); +datetime_t parse_datetime(const char * str, + optional<date_t::year_type> current_year = none); inline datetime_t parse_datetime(const std::string& str, - int current_year = -1) { + optional<date_t::year_type> current_year = none) { return parse_datetime(str.c_str(), current_year); } -date_t parse_date(const char * str, int current_year = -1); +date_t parse_date(const char * str, + optional<date_t::year_type> current_year = none); -inline date_t parse_date(const std::string& str, int current_year = -1) { +inline date_t parse_date(const std::string& str, + optional<date_t::year_type> current_year = none) { return parse_date(str.c_str(), current_year); } -inline std::time_t to_time_t(const ptime& t) -{ - if( t == posix_time::neg_infin ) - return 0; - else if( t == posix_time::pos_infin ) - return LONG_MAX; - ptime start(date(1970,1,1)); - return (t-start).total_seconds(); -} - extern std::string output_datetime_format; inline std::string format_datetime(const datetime_t& when, const optional<std::string>& format = none) { - char buf[256]; - std::time_t moment = to_time_t(when); - std::strftime(buf, 255, format ? format->c_str() : - output_datetime_format.c_str(), std::localtime(&moment)); - return buf; + posix_time::time_facet * facet + (new posix_time::time_facet(format ? format->c_str() : + output_datetime_format.c_str())); + std::ostringstream buf; + buf.imbue(std::locale(std::locale::classic(), facet)); + buf << when; + return buf.str(); } extern std::string output_date_format; @@ -121,11 +116,13 @@ extern std::string output_date_format; inline std::string format_date(const date_t& when, const optional<std::string>& format = none) { - char buf[256]; - std::tm moment = gregorian::to_tm(when); - std::strftime(buf, 255, format ? format->c_str() : - output_date_format.c_str(), &moment); - return buf; + gregorian::date_facet * facet + (new gregorian::date_facet(format ? format->c_str() : + output_date_format.c_str())); + std::ostringstream buf; + buf.imbue(std::locale(std::locale::classic(), facet)); + buf << when; + return buf.str(); } class date_interval_t : public equality_comparable<date_interval_t> diff --git a/test/regress/461980A1.test b/test/regress/461980A1.test index d78b5fb7..bbf2ee4e 100644 --- a/test/regress/461980A1.test +++ b/test/regress/461980A1.test @@ -1,6 +1,6 @@ bal <<< -2008/1/1 one +2008/01/01 one test:a 1 test:b >>>1 diff --git a/test/regress/55831A79.test b/test/regress/55831A79.test index 3f4a6348..d553bdfa 100644 --- a/test/regress/55831A79.test +++ b/test/regress/55831A79.test @@ -13,7 +13,7 @@ bal discover liabilities:credit cards:discover 4462:interest $-28.17 assets:bank:wells fargo:checking -2008/3/1 * discover card payment +2008/03/01 * discover card payment liabilities:credit cards:discover 4462 $1198.14 assets:bank:wells fargo:checking >>>1 |