Rewrote the date interval parser/stepper

The new implementation uses boost::gregorian::date_duration objects,
rather than manually stepping.
This commit is contained in:
John Wiegley 2009-03-23 01:22:26 -04:00
parent aba7a39e87
commit dda7c3a58a
5 changed files with 111 additions and 27 deletions

View file

@ -121,12 +121,12 @@ namespace {
value_t::sequence_t::const_iterator end)
{
regex date_mask(_("([0-9]+(?:[-/.][0-9]+)?(?:[-/.][0-9]+))?"));
regex dow_mask(_("(sun|mon|tue|wed|thu|fri|sat)"));
smatch what;
xact_template_t tmpl;
bool check_for_date = true;
bool check_for_date = true;
optional<date_time::weekdays> weekday;
xact_template_t::post_template_t * post = NULL;
for (; begin != end; begin++) {
@ -136,8 +136,8 @@ namespace {
check_for_date = false;
}
else if (check_for_date &&
regex_match((*begin).to_string(), what, dow_mask)) {
short dow = static_cast<short>(string_to_day_of_week(what[0]));
bool(weekday = string_to_day_of_week(what[0]))) {
short dow = static_cast<short>(*weekday);
date_t date = CURRENT_DATE() - date_duration(1);
while (date.day_of_week() != dow)
date -= date_duration(1);

View file

@ -387,8 +387,11 @@ void global_scope_t::normalize_report_options(const string& verb)
output_datetime_format = rep.HANDLER(date_format_).str() + " %H:%M:%S";
output_date_format = rep.HANDLER(date_format_).str();
}
if (rep.HANDLED(start_of_week_))
start_of_week = string_to_day_of_week(rep.HANDLER(start_of_week_).str());
if (rep.HANDLED(start_of_week_)) {
if (optional<date_time::weekdays> weekday =
string_to_day_of_week(rep.HANDLER(start_of_week_).str()))
start_of_week = *weekday;
}
// jww (2008-08-14): This code really needs to be rationalized away for 3.0.
// I might be able to do it with command objects, like register_t, which

View file

@ -35,7 +35,7 @@
namespace ledger {
int start_of_week = 0;
date_time::weekdays start_of_week = gregorian::Sunday;
optional<std::string> input_date_format;
std::string output_datetime_format = "%Y-%m-%d %H:%M:%S";
std::string output_date_format = "%Y-%m-%d";
@ -54,11 +54,6 @@ namespace {
"%Y-%m-%d",
"%m-%d",
"%Y-%m",
"%a",
"%A",
"%b",
"%B",
"%Y",
NULL
};
@ -107,7 +102,7 @@ namespace {
}
}
date_time::weekdays string_to_day_of_week(const std::string& str)
optional<date_time::weekdays> string_to_day_of_week(const std::string& str)
{
if (str == _("sun") || str == _("sunday") || str == "0")
return gregorian::Sunday;
@ -123,11 +118,41 @@ date_time::weekdays string_to_day_of_week(const std::string& str)
return gregorian::Friday;
else if (str == _("sat") || str == _("saturday") || str == "6")
return gregorian::Saturday;
assert(false);
return gregorian::Sunday;
else
return none;
}
optional<date_time::months_of_year>
string_to_month_of_year(const std::string& str)
{
if (str == _("jan") || str == _("january") || str == "0")
return gregorian::Jan;
else if (str == _("feb") || str == _("february") || str == "1")
return gregorian::Feb;
else if (str == _("mar") || str == _("march") || str == "2")
return gregorian::Mar;
else if (str == _("apr") || str == _("april") || str == "3")
return gregorian::Apr;
else if (str == _("may") || str == _("may") || str == "4")
return gregorian::May;
else if (str == _("jun") || str == _("june") || str == "5")
return gregorian::Jun;
else if (str == _("jul") || str == _("july") || str == "6")
return gregorian::Jul;
else if (str == _("aug") || str == _("august") || str == "7")
return gregorian::Aug;
else if (str == _("sep") || str == _("september") || str == "8")
return gregorian::Sep;
else if (str == _("oct") || str == _("october") || str == "9")
return gregorian::Oct;
else if (str == _("nov") || str == _("november") || str == "10")
return gregorian::Nov;
else if (str == _("dec") || str == _("december") || str == "11")
return gregorian::Dec;
else
return none;
}
datetime_t parse_datetime(const char * str, int)
{
std::tm when;
@ -335,11 +360,16 @@ bool date_interval_t::find_period(const date_t& date)
return false;
}
if (end_of_duration && date < *end_of_duration) {
DEBUG("times.interval",
"true: date [" << date << "] < end_of_duration ["
<< *end_of_duration << "]");
return true;
if (end_of_duration) {
if (date < *end_of_duration) {
DEBUG("times.interval",
"true: date [" << date << "] < end_of_duration ["
<< *end_of_duration << "]");
return true;
}
} else {
DEBUG("times.interval", "false: there is no end_of_duration");
return false;
}
// If we've reached here, it means the date does not fall into the current
@ -524,6 +554,10 @@ void date_interval_t::parse(std::istream& in)
{
string word;
optional<date_time::months_of_year> mon;
optional<date_time::weekdays> wday;
optional<date_t::year_type> year;
while (! in.eof()) {
read_lower_word(in, word);
if (word == _("every")) {
@ -583,13 +617,50 @@ void date_interval_t::parse(std::istream& in)
read_lower_word(in, word);
parse_date_words(in, word, *this, false, true);
}
else if (optional<date_time::months_of_year>
m = string_to_month_of_year(word)) {
mon = m;
}
else if (optional<date_time::weekdays>
d = string_to_day_of_week(word)) {
wday = d;
}
else if (all(word, is_digit())) {
year = lexical_cast<unsigned short>(word);
}
else {
// otherwise, it should be an explicit date
date_t b, e;
parse_inclusion_specifier(word, &b, &e);
start = b;
end = e;
}
}
if (year || mon || wday) {
if (! start)
start = CURRENT_DATE();
if (wday) {
while (start->day_of_week() != *wday)
*start -= gregorian::days(1);
if (! end)
end = *start + gregorian::days(1);
} else {
if (year) {
start = date_t(*year, 1, 1);
if (! end)
end = *start + gregorian::years(1);
}
if (mon) {
start = date_t(start->year(), *mon, 1);
if (! end)
end = *start + gregorian::months(1);
}
}
}
}
} // namespace ledger

View file

@ -73,14 +73,18 @@ inline bool is_valid(const date_t& moment) {
#endif
#define CURRENT_DATE() boost::gregorian::day_clock::universal_day()
extern int start_of_week;
extern date_time::weekdays start_of_week;
extern optional<std::string> input_date_format;
date_time::weekdays string_to_day_of_week(const std::string& str);
optional<date_time::weekdays>
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);
inline datetime_t parse_datetime(const std::string& str, int current_year = -1) {
inline datetime_t parse_datetime(const std::string& str,
int current_year = -1) {
return parse_datetime(str.c_str(), current_year);
}

View file

@ -28,12 +28,14 @@ void DateTimeTestCase::testConstructors()
date_t d8;
date_t d9;
#if 0
date_t d10;
date_t d11;
date_t d12;
date_t d13;
date_t d14;
datetime_t d15;
#endif
#endif // NOT_FOR_PYTHON
d1 = parse_date("1990/01/01");
@ -47,13 +49,15 @@ void DateTimeTestCase::testConstructors()
d8 = parse_date("2006-12-25");
d9 = parse_date("12-25");
#ifndef NOT_FOR_PYTHON
#if 0
d10 = parse_date("tue");
d11 = parse_date("tuesday");
d12 = parse_date("feb");
d13 = parse_date("february");
d14 = parse_date("2006");
#ifndef NOT_FOR_PYTHON
d15 = d3;
#endif
#endif // NOT_FOR_PYTHON
#ifndef NOT_FOR_PYTHON
@ -66,17 +70,19 @@ void DateTimeTestCase::testConstructors()
assertTrue(CURRENT_DATE() > d4);
#ifndef NOT_FOR_PYTHON
#if 0
assertEqual(d3, d15);
#endif
#endif // NOT_FOR_PYTHON
assertEqual(d4, d6);
assertEqual(d4, d8);
assertEqual(d5, d7);
assertEqual(d5, d9);
#ifndef NOT_FOR_PYTHON
#if 0
assertEqual(d10, d11);
assertEqual(d12, d13);
#if 0
#ifndef NOT_FOR_PYTHON
assertThrow(parse_date("2007/02/29"), boost::gregorian::bad_day_of_month);
//assertThrow(parse_date("2007/13/01"), datetime_error);
//assertThrow(parse_date("2007/00/01"), datetime_error);