summaryrefslogtreecommitdiff
path: root/src/times.cc
diff options
context:
space:
mode:
authorJohn Wiegley <johnw@newartisans.com>2010-06-21 17:02:48 -0400
committerJohn Wiegley <johnw@newartisans.com>2010-06-21 17:02:48 -0400
commitfcfa491485efececdea05617e6498e43e235b702 (patch)
tree8b006216d7f7dca77704fd86f628b515fbf7fb1f /src/times.cc
parent009e07690db50b4e0fb85a2e5ed876bb0dc4235e (diff)
downloadfork-ledger-fcfa491485efececdea05617e6498e43e235b702.tar.gz
fork-ledger-fcfa491485efececdea05617e6498e43e235b702.tar.bz2
fork-ledger-fcfa491485efececdea05617e6498e43e235b702.zip
Improvements to time period parsing
Things like "since last month" and "4 weeks ago", and "since 4 weeks ago" are now all working.
Diffstat (limited to 'src/times.cc')
-rw-r--r--src/times.cc198
1 files changed, 190 insertions, 8 deletions
diff --git a/src/times.cc b/src/times.cc
index d84cb238..b8dcf98f 100644
--- a/src/times.cc
+++ b/src/times.cc
@@ -408,6 +408,8 @@ class date_parser_t
TOK_A_MONTH,
TOK_A_WDAY,
+ TOK_AGO,
+ TOK_HENCE,
TOK_SINCE,
TOK_UNTIL,
TOK_IN,
@@ -505,6 +507,8 @@ class date_parser_t
out << date_specifier_t::day_of_week_type
(boost::get<date_time::weekdays>(*value));
break;
+ case TOK_AGO: return "ago";
+ case TOK_HENCE: return "hence";
case TOK_SINCE: return "since";
case TOK_UNTIL: return "until";
case TOK_IN: return "in";
@@ -552,6 +556,8 @@ class date_parser_t
case TOK_A_YEAR: out << "TOK_A_YEAR"; break;
case TOK_A_MONTH: out << "TOK_A_MONTH"; break;
case TOK_A_WDAY: out << "TOK_A_WDAY"; break;
+ case TOK_AGO: out << "TOK_AGO"; break;
+ case TOK_HENCE: out << "TOK_HENCE"; break;
case TOK_SINCE: out << "TOK_SINCE"; break;
case TOK_UNTIL: out << "TOK_UNTIL"; break;
case TOK_IN: out << "TOK_IN"; break;
@@ -645,17 +651,182 @@ private:
void date_parser_t::determine_when(date_parser_t::lexer_t::token_t& tok,
date_specifier_t& specifier)
{
+ date_t today = CURRENT_DATE();
+
switch (tok.kind) {
case lexer_t::token_t::TOK_DATE:
specifier = boost::get<date_specifier_t>(*tok.value);
break;
- case lexer_t::token_t::TOK_INT:
- specifier.day =
- date_specifier_t::day_type(boost::get<unsigned short>(*tok.value));
+ case lexer_t::token_t::TOK_INT: {
+ unsigned short amount = boost::get<unsigned short>(*tok.value);
+ int8_t adjust = 0;
+
+ tok = lexer.peek_token();
+ lexer_t::token_t::kind_t kind = tok.kind;
+ switch (kind) {
+ case lexer_t::token_t::TOK_YEAR:
+ case lexer_t::token_t::TOK_YEARS:
+ case lexer_t::token_t::TOK_QUARTER:
+ case lexer_t::token_t::TOK_QUARTERS:
+ case lexer_t::token_t::TOK_MONTH:
+ case lexer_t::token_t::TOK_MONTHS:
+ case lexer_t::token_t::TOK_WEEK:
+ case lexer_t::token_t::TOK_WEEKS:
+ case lexer_t::token_t::TOK_DAY:
+ case lexer_t::token_t::TOK_DAYS:
+ lexer.next_token();
+ tok = lexer.next_token();
+ switch (tok.kind) {
+ case lexer_t::token_t::TOK_AGO:
+ adjust = -1;
+ break;
+ case lexer_t::token_t::TOK_HENCE:
+ adjust = 1;
+ break;
+ default:
+ tok.unexpected();
+ break;
+ }
+ break;
+ default:
+ break;
+ }
+
+ date_t when(today);
+
+ switch (kind) {
+ case lexer_t::token_t::TOK_YEAR:
+ case lexer_t::token_t::TOK_YEARS:
+ when += gregorian::years(amount * adjust);
+ break;
+ case lexer_t::token_t::TOK_QUARTER:
+ case lexer_t::token_t::TOK_QUARTERS: {
+ date_t temp =
+ date_duration_t::find_nearest(today, date_duration_t::QUARTERS);
+ when += gregorian::months(amount * 3 * adjust);
+ break;
+ }
+ case lexer_t::token_t::TOK_MONTH:
+ case lexer_t::token_t::TOK_MONTHS:
+ when += gregorian::months(amount * adjust);
+ break;
+ case lexer_t::token_t::TOK_WEEK:
+ case lexer_t::token_t::TOK_WEEKS:
+ when += gregorian::weeks(amount * adjust);
+ break;
+ case lexer_t::token_t::TOK_DAY:
+ case lexer_t::token_t::TOK_DAYS:
+ when += gregorian::days(amount * adjust);
+ break;
+ default:
+ specifier.day = date_specifier_t::day_type(amount);
+ break;
+ }
+
+ if (adjust)
+ specifier = date_specifier_t(when);
+ break;
+ }
+
+ case lexer_t::token_t::TOK_THIS:
+ case lexer_t::token_t::TOK_NEXT:
+ case lexer_t::token_t::TOK_LAST: {
+ int8_t adjust = 0;
+ if (tok.kind == lexer_t::token_t::TOK_NEXT)
+ adjust = 1;
+ else if (tok.kind == lexer_t::token_t::TOK_LAST)
+ adjust = -1;
+
+ tok = lexer.next_token();
+ switch (tok.kind) {
+ case lexer_t::token_t::TOK_A_MONTH: {
+ date_t temp(today.year(),
+ boost::get<date_time::months_of_year>(*tok.value), 1);
+ temp += gregorian::years(adjust);
+ specifier =
+ date_specifier_t(static_cast<date_specifier_t::year_type>(temp.year()),
+ temp.month());
+ break;
+ }
+
+ case lexer_t::token_t::TOK_A_WDAY: {
+ date_t temp =
+ date_duration_t::find_nearest(today, date_duration_t::WEEKS);
+ while (temp.day_of_week() !=
+ boost::get<date_time::months_of_year>(*tok.value))
+ temp += gregorian::days(1);
+ temp += gregorian::days(7 * adjust);
+ specifier = date_specifier_t(temp);
+ break;
+ }
+
+ case lexer_t::token_t::TOK_YEAR: {
+ date_t temp(today);
+ temp += gregorian::years(adjust);
+ specifier =
+ date_specifier_t(static_cast<date_specifier_t::year_type>(temp.year()));
+ break;
+ }
+
+ case lexer_t::token_t::TOK_QUARTER: {
+ date_t base =
+ date_duration_t::find_nearest(today, date_duration_t::QUARTERS);
+ date_t temp;
+ if (adjust < 0) {
+ temp = base + gregorian::months(3 * adjust);
+ }
+ else if (adjust == 0) {
+ temp = base + gregorian::months(3);
+ }
+ else if (adjust > 0) {
+ base += gregorian::months(3 * adjust);
+ temp = base + gregorian::months(3 * adjust);
+ }
+ specifier = date_specifier_t(adjust < 0 ? temp : base);
+ break;
+ }
+
+ case lexer_t::token_t::TOK_WEEK: {
+ date_t base =
+ date_duration_t::find_nearest(today, date_duration_t::WEEKS);
+ date_t temp;
+ if (adjust < 0) {
+ temp = base + gregorian::days(7 * adjust);
+ }
+ else if (adjust == 0) {
+ temp = base + gregorian::days(7);
+ }
+ else if (adjust > 0) {
+ base += gregorian::days(7 * adjust);
+ temp = base + gregorian::days(7 * adjust);
+ }
+ specifier = date_specifier_t(adjust < 0 ? temp : base);
+ break;
+ }
+
+ case lexer_t::token_t::TOK_DAY: {
+ date_t temp(today);
+ temp += gregorian::days(adjust);
+ specifier = date_specifier_t(temp);
+ break;
+ }
+
+ default:
+ case lexer_t::token_t::TOK_MONTH: {
+ date_t temp(today);
+ temp += gregorian::months(adjust);
+ specifier =
+ date_specifier_t(static_cast<date_specifier_t::year_type>(temp.year()),
+ temp.month());
+ break;
+ }
+ }
break;
+ }
+
case lexer_t::token_t::TOK_A_YEAR:
- specifier.year = boost::get<date_specifier_t::year_type>(*tok.value);
+ specifier.year = boost::get<date_specifier_t::year_type>(*tok.value);
break;
case lexer_t::token_t::TOK_A_MONTH:
specifier.month =
@@ -669,13 +840,13 @@ void date_parser_t::determine_when(date_parser_t::lexer_t::token_t& tok,
break;
case lexer_t::token_t::TOK_TODAY:
- specifier = date_specifier_t(CURRENT_DATE());
+ specifier = date_specifier_t(today);
break;
case lexer_t::token_t::TOK_TOMORROW:
- specifier = date_specifier_t(CURRENT_DATE() + gregorian::days(1));
+ specifier = date_specifier_t(today + gregorian::days(1));
break;
case lexer_t::token_t::TOK_YESTERDAY:
- specifier = date_specifier_t(CURRENT_DATE() - gregorian::days(1));
+ specifier = date_specifier_t(today - gregorian::days(1));
break;
default:
@@ -791,6 +962,9 @@ date_interval_t date_parser_t::parse()
date_t base(today);
date_t end(today);
+ if (! adjust)
+ adjust = 1;
+
tok = lexer.next_token();
switch (tok.kind) {
case lexer_t::token_t::TOK_YEARS:
@@ -1290,7 +1464,11 @@ void date_interval_t::dump(std::ostream& out)
if (duration)
out << _("duration: ") << duration->to_string() << std::endl;
- stabilize(begin());
+ optional<date_t> when(begin());
+ if (! when)
+ when = CURRENT_DATE();
+
+ stabilize(when);
out << std::endl
<< _("--- After stabilization ---") << std::endl;
@@ -1410,6 +1588,10 @@ date_parser_t::lexer_t::token_t date_parser_t::lexer_t::next_token()
string_to_day_of_week(term)) {
return token_t(token_t::TOK_A_WDAY, token_t::content_t(*wday));
}
+ else if (term == _("ago"))
+ return token_t(token_t::TOK_AGO);
+ else if (term == _("hence"))
+ return token_t(token_t::TOK_HENCE);
else if (term == _("from") || term == _("since"))
return token_t(token_t::TOK_SINCE);
else if (term == _("to") || term == _("until"))