summaryrefslogtreecommitdiff
path: root/src/times.cc
diff options
context:
space:
mode:
authorJohn Wiegley <johnw@newartisans.com>2009-10-11 05:19:01 -0400
committerJohn Wiegley <johnw@newartisans.com>2009-10-11 05:19:01 -0400
commitf161aea8ce96c20f888cd65a78e178e5f5552dc2 (patch)
treeb8f9279c611fe08f1b10bb885144f7c275c7561d /src/times.cc
parent1a8e835bfe2cb18cd10691d7a4ab0fc478c4aced (diff)
downloadfork-ledger-f161aea8ce96c20f888cd65a78e178e5f5552dc2.tar.gz
fork-ledger-f161aea8ce96c20f888cd65a78e178e5f5552dc2.tar.bz2
fork-ledger-f161aea8ce96c20f888cd65a78e178e5f5552dc2.zip
Removed reliance on strptime/strftime
The code now uses Boost's input and output facets for times and dates. This ensures completely consistency regarding timezones and times, and fixes the regression test that was broken while I was away coding in London (where it was GMT-0 and I didn't notice the difference between local and GMT).
Diffstat (limited to 'src/times.cc')
-rw-r--r--src/times.cc174
1 files changed, 84 insertions, 90 deletions
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;
}
}