diff options
author | John Wiegley <johnw@newartisans.com> | 2010-06-21 17:02:48 -0400 |
---|---|---|
committer | John Wiegley <johnw@newartisans.com> | 2010-06-21 17:02:48 -0400 |
commit | fcfa491485efececdea05617e6498e43e235b702 (patch) | |
tree | 8b006216d7f7dca77704fd86f628b515fbf7fb1f /src/times.cc | |
parent | 009e07690db50b4e0fb85a2e5ed876bb0dc4235e (diff) | |
download | fork-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.cc | 198 |
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")) |