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).
This commit is contained in:
parent
1a8e835bfe
commit
f161aea8ce
9 changed files with 122 additions and 126 deletions
|
|
@ -114,7 +114,7 @@ void item_t::set_tag(const string& tag,
|
||||||
assert(result.second);
|
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 * b = std::strchr(p, '[')) {
|
||||||
if (const char * e = 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) {
|
if (note) {
|
||||||
*note += '\n';
|
*note += '\n';
|
||||||
|
|
|
||||||
|
|
@ -129,8 +129,10 @@ public:
|
||||||
virtual void set_tag(const string& tag,
|
virtual void set_tag(const string& tag,
|
||||||
const optional<string>& value = none);
|
const optional<string>& value = none);
|
||||||
|
|
||||||
virtual void parse_tags(const char * p, int current_year = -1);
|
virtual void parse_tags(const char * p,
|
||||||
virtual void append_note(const char * p, int current_year = -1);
|
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;
|
static bool use_effective_date;
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -67,7 +67,7 @@ class session_t : public symbol_scope_t
|
||||||
|
|
||||||
public:
|
public:
|
||||||
bool flush_on_next_data_file;
|
bool flush_on_next_data_file;
|
||||||
int current_year;
|
date_t::year_type current_year;
|
||||||
|
|
||||||
shared_ptr<commodity_pool_t> commodity_pool;
|
shared_ptr<commodity_pool_t> commodity_pool;
|
||||||
scoped_ptr<account_t> master;
|
scoped_ptr<account_t> master;
|
||||||
|
|
|
||||||
|
|
@ -53,6 +53,7 @@
|
||||||
#include <algorithm>
|
#include <algorithm>
|
||||||
#include <exception>
|
#include <exception>
|
||||||
#include <typeinfo>
|
#include <typeinfo>
|
||||||
|
#include <locale>
|
||||||
#include <stdexcept>
|
#include <stdexcept>
|
||||||
#include <iostream>
|
#include <iostream>
|
||||||
#include <streambuf>
|
#include <streambuf>
|
||||||
|
|
@ -98,7 +99,6 @@ typedef std::ostream::pos_type ostream_pos_type;
|
||||||
#include <cstdio>
|
#include <cstdio>
|
||||||
#include <cstdlib>
|
#include <cstdlib>
|
||||||
#include <cstring>
|
#include <cstring>
|
||||||
#include <ctime>
|
|
||||||
#include <csignal>
|
#include <csignal>
|
||||||
|
|
||||||
#if defined __FreeBSD__ && __FreeBSD__ <= 4
|
#if defined __FreeBSD__ && __FreeBSD__ <= 4
|
||||||
|
|
@ -144,6 +144,8 @@ typedef std::ostream::pos_type ostream_pos_type;
|
||||||
#include <boost/cast.hpp>
|
#include <boost/cast.hpp>
|
||||||
#include <boost/current_function.hpp>
|
#include <boost/current_function.hpp>
|
||||||
#include <boost/date_time/posix_time/posix_time.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/convenience.hpp>
|
||||||
#include <boost/filesystem/exception.hpp>
|
#include <boost/filesystem/exception.hpp>
|
||||||
#include <boost/filesystem/fstream.hpp>
|
#include <boost/filesystem/fstream.hpp>
|
||||||
|
|
|
||||||
|
|
@ -65,7 +65,6 @@ namespace {
|
||||||
account_t * master;
|
account_t * master;
|
||||||
const path * original_file;
|
const path * original_file;
|
||||||
accounts_map account_aliases;
|
accounts_map account_aliases;
|
||||||
int current_year;
|
|
||||||
bool strict;
|
bool strict;
|
||||||
|
|
||||||
path pathname;
|
path pathname;
|
||||||
|
|
@ -76,6 +75,8 @@ namespace {
|
||||||
std::size_t count;
|
std::size_t count;
|
||||||
std::size_t errors;
|
std::size_t errors;
|
||||||
|
|
||||||
|
optional<date_t::year_type> current_year;
|
||||||
|
|
||||||
scoped_ptr<auto_xact_finalizer_t> auto_xact_finalizer;
|
scoped_ptr<auto_xact_finalizer_t> auto_xact_finalizer;
|
||||||
|
|
||||||
instance_t(std::list<account_t *>& _account_stack,
|
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)
|
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)
|
void instance_t::option_directive(char * line)
|
||||||
|
|
|
||||||
184
src/times.cc
184
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";
|
std::string output_date_format = "%Y-%m-%d";
|
||||||
|
|
||||||
namespace {
|
namespace {
|
||||||
const char * formats[] = {
|
struct date_format_t {
|
||||||
"%y/%m/%d",
|
const char * format;
|
||||||
"%Y/%m/%d",
|
bool has_year;
|
||||||
"%m/%d",
|
date_format_t(const char * _format, bool _has_year)
|
||||||
"%Y/%m",
|
: format(_format), has_year(_has_year) {}
|
||||||
"%y.%m.%d",
|
|
||||||
"%Y.%m.%d",
|
|
||||||
"%m.%d",
|
|
||||||
"%Y.%m",
|
|
||||||
"%y-%m-%d",
|
|
||||||
"%Y-%m-%d",
|
|
||||||
"%m-%d",
|
|
||||||
"%Y-%m",
|
|
||||||
NULL
|
|
||||||
};
|
};
|
||||||
|
|
||||||
bool parse_date_mask(const char * date_str, std::tm& result)
|
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)
|
||||||
|
};
|
||||||
|
|
||||||
|
date_t parse_date_mask_routine(const char * date_str, const date_format_t& df,
|
||||||
|
optional<date_t::year_type> year, bool& saw_year)
|
||||||
|
{
|
||||||
|
std::string str(date_str);
|
||||||
|
|
||||||
|
gregorian::date_input_facet * facet(new gregorian::date_input_facet(df.format));
|
||||||
|
std::istringstream sstr(str);
|
||||||
|
sstr.imbue(std::locale(sstr.getloc(), facet));
|
||||||
|
|
||||||
|
date_t when;
|
||||||
|
sstr >> when;
|
||||||
|
|
||||||
|
if (! when.is_not_a_date()) {
|
||||||
|
if (sstr.good() && ! sstr.eof() && sstr.peek() != EOF)
|
||||||
|
return date_t();
|
||||||
|
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
|
||||||
|
date_t parse_date_mask(const char * date_str, optional<date_t::year_type> year,
|
||||||
|
bool& saw_year)
|
||||||
{
|
{
|
||||||
if (input_date_format) {
|
if (input_date_format) {
|
||||||
std::memset(&result, -1, sizeof(std::tm));
|
date_format_t df(input_date_format->c_str(), true);
|
||||||
if (strptime(date_str, input_date_format->c_str(), &result))
|
if (! icontains(*input_date_format, "%y"))
|
||||||
return true;
|
df.has_year = false;
|
||||||
}
|
date_t when = parse_date_mask_routine(date_str, df, year, saw_year);
|
||||||
for (const char ** f = formats; *f; f++) {
|
if (! when.is_not_a_date())
|
||||||
std::memset(&result, -1, sizeof(std::tm));
|
return when;
|
||||||
if (strptime(date_str, *f, &result))
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
return false;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
bool quick_parse_date(const char * date_str, std::tm& result, const int 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,
|
||||||
if (! parse_date_mask(date_str, result))
|
saw_year);
|
||||||
return false;
|
if (! when.is_not_a_date())
|
||||||
|
return when;
|
||||||
result.tm_hour = 0;
|
|
||||||
result.tm_min = 0;
|
|
||||||
result.tm_sec = 0;
|
|
||||||
|
|
||||||
if (result.tm_mday == -1)
|
|
||||||
result.tm_mday = 1;
|
|
||||||
|
|
||||||
if (result.tm_mon == -1) {
|
|
||||||
result.tm_mon = 0;
|
|
||||||
|
|
||||||
if (result.tm_mday > (CURRENT_DATE().day() - 1))
|
|
||||||
result.tm_mon = 11;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (result.tm_year == -1) {
|
return date_t();
|
||||||
result.tm_year = (year == -1 ? int(CURRENT_DATE().year()) : year) - 1900;
|
|
||||||
|
|
||||||
if (year == -1 && result.tm_mon > (CURRENT_DATE().month() - 1))
|
|
||||||
result.tm_year--;
|
|
||||||
}
|
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -153,21 +172,24 @@ string_to_month_of_year(const std::string& str)
|
||||||
return none;
|
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;
|
posix_time::time_input_facet * facet
|
||||||
std::memset(&when, -1, sizeof(std::tm));
|
(new posix_time::time_input_facet("%Y/%m/%d %H:%M:%S"));
|
||||||
if (strptime(str, "%Y/%m/%d %H:%M:%S", &when))
|
|
||||||
return posix_time::ptime_from_tm(when);
|
std::string temp(str);
|
||||||
else
|
std::istringstream sstr(temp);
|
||||||
return datetime_t();
|
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;
|
bool saw_year;
|
||||||
quick_parse_date(str, when, current_year);
|
return parse_date_mask(str, current_year, saw_year);
|
||||||
return gregorian::date_from_tm(when);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
date_t date_interval_t::add_duration(const date_t& date,
|
date_t date_interval_t::add_duration(const date_t& date,
|
||||||
|
|
@ -436,52 +458,24 @@ namespace {
|
||||||
date_t * begin,
|
date_t * begin,
|
||||||
date_t * end)
|
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);
|
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) {
|
if (begin) {
|
||||||
*begin = gregorian::date_from_tm(when);
|
*begin = when;
|
||||||
|
|
||||||
if (end) {
|
if (end) {
|
||||||
if (saw_year)
|
if (saw_year)
|
||||||
*end = *begin + gregorian::years(1);
|
*end = *begin + gregorian::years(1);
|
||||||
else if (saw_mon)
|
else
|
||||||
*end = *begin + gregorian::months(1);
|
*end = *begin + gregorian::months(1);
|
||||||
else if (saw_day)
|
|
||||||
*end = *begin + gregorian::days(1);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else if (end) {
|
else if (end) {
|
||||||
*end = gregorian::date_from_tm(when);
|
*end = when;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
45
src/times.h
45
src/times.h
|
|
@ -81,39 +81,34 @@ string_to_day_of_week(const std::string& str);
|
||||||
optional<date_time::months_of_year>
|
optional<date_time::months_of_year>
|
||||||
string_to_month_of_year(const std::string& str);
|
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,
|
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);
|
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);
|
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;
|
extern std::string output_datetime_format;
|
||||||
|
|
||||||
inline std::string format_datetime(const datetime_t& when,
|
inline std::string format_datetime(const datetime_t& when,
|
||||||
const optional<std::string>& format = none)
|
const optional<std::string>& format = none)
|
||||||
{
|
{
|
||||||
char buf[256];
|
posix_time::time_facet * facet
|
||||||
std::time_t moment = to_time_t(when);
|
(new posix_time::time_facet(format ? format->c_str() :
|
||||||
std::strftime(buf, 255, format ? format->c_str() :
|
output_datetime_format.c_str()));
|
||||||
output_datetime_format.c_str(), std::localtime(&moment));
|
std::ostringstream buf;
|
||||||
return buf;
|
buf.imbue(std::locale(std::locale::classic(), facet));
|
||||||
|
buf << when;
|
||||||
|
return buf.str();
|
||||||
}
|
}
|
||||||
|
|
||||||
extern std::string output_date_format;
|
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,
|
inline std::string format_date(const date_t& when,
|
||||||
const optional<std::string>& format = none)
|
const optional<std::string>& format = none)
|
||||||
{
|
{
|
||||||
char buf[256];
|
gregorian::date_facet * facet
|
||||||
std::tm moment = gregorian::to_tm(when);
|
(new gregorian::date_facet(format ? format->c_str() :
|
||||||
std::strftime(buf, 255, format ? format->c_str() :
|
output_date_format.c_str()));
|
||||||
output_date_format.c_str(), &moment);
|
std::ostringstream buf;
|
||||||
return buf;
|
buf.imbue(std::locale(std::locale::classic(), facet));
|
||||||
|
buf << when;
|
||||||
|
return buf.str();
|
||||||
}
|
}
|
||||||
|
|
||||||
class date_interval_t : public equality_comparable<date_interval_t>
|
class date_interval_t : public equality_comparable<date_interval_t>
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,6 @@
|
||||||
bal
|
bal
|
||||||
<<<
|
<<<
|
||||||
2008/1/1 one
|
2008/01/01 one
|
||||||
test:a 1
|
test:a 1
|
||||||
test:b
|
test:b
|
||||||
>>>1
|
>>>1
|
||||||
|
|
|
||||||
|
|
@ -13,7 +13,7 @@ bal discover
|
||||||
liabilities:credit cards:discover 4462:interest $-28.17
|
liabilities:credit cards:discover 4462:interest $-28.17
|
||||||
assets:bank:wells fargo:checking
|
assets:bank:wells fargo:checking
|
||||||
|
|
||||||
2008/3/1 * discover card payment
|
2008/03/01 * discover card payment
|
||||||
liabilities:credit cards:discover 4462 $1198.14
|
liabilities:credit cards:discover 4462 $1198.14
|
||||||
assets:bank:wells fargo:checking
|
assets:bank:wells fargo:checking
|
||||||
>>>1
|
>>>1
|
||||||
|
|
|
||||||
Loading…
Add table
Reference in a new issue