summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--src/filters.cc76
-rw-r--r--src/filters.h40
-rw-r--r--src/precmd.cc53
-rw-r--r--src/report.h12
-rw-r--r--src/times.cc336
-rw-r--r--src/times.h132
-rw-r--r--src/token.cc7
-rw-r--r--src/xact.h6
8 files changed, 389 insertions, 273 deletions
diff --git a/src/filters.cc b/src/filters.cc
index d0e345ae..06518169 100644
--- a/src/filters.cc
+++ b/src/filters.cc
@@ -562,7 +562,7 @@ void interval_posts::report_subtotal(const date_t& finish)
if (exact_periods)
subtotal_posts::report_subtotal();
else
- subtotal_posts::report_subtotal(NULL, interval.begin, finish);
+ subtotal_posts::report_subtotal(NULL, *interval.start, finish);
}
last_post = NULL;
@@ -572,49 +572,37 @@ void interval_posts::operator()(post_t& post)
{
date_t date = post.date();
- if ((is_valid(interval.begin) && date < interval.begin) ||
- (is_valid(interval.end) && date >= interval.end))
+ if (! interval.find_period(post.date(), &last_interval))
return;
- if (interval) {
- if (! is_valid(interval.begin))
- interval.set_start(date);
- start = interval.begin;
-
- date_t quant = interval.increment(interval.begin);
- if (date >= quant) {
- if (last_post)
- report_subtotal(quant - gregorian::days(1));
-
- date_t temp;
- while (date >= (temp = interval.increment(quant))) {
- if (quant == temp)
- break;
- interval.begin = quant;
- quant = temp;
+ if (interval.duration) {
+ if (last_interval) {
+ if (interval != last_interval) {
+ report_subtotal(last_interval.inclusive_end());
if (generate_empty_posts) {
- // Generate a null posting, so the intervening periods can be
- // seen when -E is used, or if the calculated amount ends up being
- // non-zero
- xact_temps.push_back(xact_t());
- xact_t& null_xact = xact_temps.back();
- null_xact.add_flags(ITEM_TEMP);
- null_xact._date = quant - gregorian::days(1);
-
- post_temps.push_back(post_t(&empty_account));
- post_t& null_post = post_temps.back();
- null_post.add_flags(ITEM_TEMP | POST_CALCULATED);
- null_post.amount = 0L;
- null_xact.add_post(&null_post);
-
- last_post = &null_post;
- subtotal_posts::operator()(null_post);
-
- report_subtotal(quant - gregorian::days(1));
+ for (++last_interval; interval != last_interval; ++last_interval) {
+ // Generate a null posting, so the intervening periods can be
+ // seen when -E is used, or if the calculated amount ends up being
+ // non-zero
+ xact_temps.push_back(xact_t());
+ xact_t& null_xact = xact_temps.back();
+ null_xact.add_flags(ITEM_TEMP);
+ null_xact._date = last_interval.inclusive_end();
+
+ post_temps.push_back(post_t(&empty_account));
+ post_t& null_post = post_temps.back();
+ null_post.add_flags(ITEM_TEMP | POST_CALCULATED);
+ null_post.amount = 0L;
+ null_xact.add_post(&null_post);
+
+ last_post = &null_post;
+ subtotal_posts::operator()(null_post);
+
+ report_subtotal(last_interval.inclusive_end());
+ }
}
}
- start = interval.begin = quant;
}
subtotal_posts::operator()(post);
} else {
@@ -745,7 +733,7 @@ void generate_posts::add_period_xacts(period_xacts_list& period_xacts)
add_post(xact->period, *post);
}
-void generate_posts::add_post(const interval_t& period, post_t& post)
+void generate_posts::add_post(const date_interval_t& period, post_t& post)
{
pending_posts.push_back(pending_posts_pair(period, &post));
}
@@ -759,6 +747,7 @@ void budget_posts::report_budget_items(const date_t& date)
do {
reported = false;
foreach (pending_posts_list::value_type& pair, pending_posts) {
+#if 0
date_t& begin = pair.first.begin;
if (! is_valid(begin)) {
pair.first.set_start(date);
@@ -790,6 +779,7 @@ void budget_posts::report_budget_items(const date_t& date)
reported = true;
}
+#endif
}
} while (reported);
}
@@ -823,11 +813,12 @@ void budget_posts::operator()(post_t& post)
}
}
-void forecast_posts::add_post(const interval_t& period, post_t& post)
+void forecast_posts::add_post(const date_interval_t& period, post_t& post)
{
generate_posts::add_post(period, post);
- interval_t& i = pending_posts.back().first;
+ date_interval_t& i = pending_posts.back().first;
+#if 0
if (! is_valid(i.begin)) {
i.set_start(CURRENT_DATE());
i.begin = i.increment(i.begin);
@@ -835,6 +826,7 @@ void forecast_posts::add_post(const interval_t& period, post_t& post)
while (i.begin < CURRENT_DATE())
i.begin = i.increment(i.begin);
}
+#endif
}
void forecast_posts::flush()
@@ -842,6 +834,7 @@ void forecast_posts::flush()
posts_list passed;
date_t last;
+#if 0
while (pending_posts.size() > 0) {
pending_posts_list::iterator least = pending_posts.begin();
for (pending_posts_list::iterator i = ++pending_posts.begin();
@@ -900,6 +893,7 @@ void forecast_posts::flush()
}
}
}
+#endif
item_handler<post_t>::flush();
}
diff --git a/src/filters.h b/src/filters.h
index e4d26ca9..994d7b36 100644
--- a/src/filters.h
+++ b/src/filters.h
@@ -558,22 +558,22 @@ public:
*/
class interval_posts : public subtotal_posts
{
- interval_t interval;
- post_t * last_post;
- account_t empty_account;
- bool exact_periods;
- bool generate_empty_posts;
- date_t start;
+ date_interval_t interval;
+ date_interval_t last_interval;
+ post_t * last_post;
+ account_t empty_account;
+ bool exact_periods;
+ bool generate_empty_posts;
interval_posts();
public:
- interval_posts(post_handler_ptr _handler,
- expr_t& amount_expr,
- const interval_t& _interval,
- bool _exact_periods = false,
- bool _generate_empty_posts = false)
+ interval_posts(post_handler_ptr _handler,
+ expr_t& amount_expr,
+ const date_interval_t& _interval,
+ bool _exact_periods = false,
+ bool _generate_empty_posts = false)
: subtotal_posts(_handler, amount_expr), interval(_interval),
last_post(NULL), empty_account(NULL, _("<None>")),
exact_periods(_exact_periods),
@@ -588,9 +588,11 @@ public:
void report_subtotal(const date_t& finish);
virtual void flush() {
- if (last_post && interval)
- report_subtotal(interval.increment(interval.begin) - gregorian::days(1));
- subtotal_posts::flush();
+ if (last_post && interval.duration) {
+ if (interval.is_valid())
+ report_subtotal(interval.inclusive_end());
+ subtotal_posts::flush();
+ }
}
virtual void operator()(post_t& post);
};
@@ -725,11 +727,11 @@ class generate_posts : public item_handler<post_t>
generate_posts();
protected:
- typedef std::pair<interval_t, post_t *> pending_posts_pair;
- typedef std::list<pending_posts_pair> pending_posts_list;
+ typedef std::pair<date_interval_t, post_t *> pending_posts_pair;
+ typedef std::list<pending_posts_pair> pending_posts_list;
pending_posts_list pending_posts;
- std::list<xact_t> xact_temps;
+ std::list<xact_t> xact_temps;
std::list<post_t> post_temps;
public:
@@ -745,7 +747,7 @@ public:
void add_period_xacts(period_xacts_list& period_xacts);
- virtual void add_post(const interval_t& period, post_t& post);
+ virtual void add_post(const date_interval_t& period, post_t& post);
};
/**
@@ -800,7 +802,7 @@ class forecast_posts : public generate_posts
TRACE_DTOR(forecast_posts);
}
- virtual void add_post(const interval_t& period, post_t& post);
+ virtual void add_post(const date_interval_t& period, post_t& post);
virtual void flush();
};
diff --git a/src/precmd.cc b/src/precmd.cc
index ef04cb79..c3e74c0e 100644
--- a/src/precmd.cc
+++ b/src/precmd.cc
@@ -160,25 +160,54 @@ value_t period_command(call_scope_t& args)
report_t& report(find_scope<report_t>(args));
std::ostream& out(report.output_stream);
- interval_t interval(arg);
+ date_interval_t interval(arg);
+
+ out << _("global details => ") << std::endl << std::endl;
+
+ if (interval.start)
+ out << _(" start: ") << format_date(*interval.start) << std::endl;
+ else
+ out << _(" start: TODAY: ") << format_date(CURRENT_DATE()) << std::endl;
+ if (interval.end)
+ out << _(" end: ") << format_date(*interval.end) << std::endl;
+
+ if (interval.skip_duration)
+ out << _(" skip: ") << *interval.skip_duration << std::endl;
+ if (interval.factor)
+ out << _(" factor: ") << interval.factor << std::endl;
+ if (interval.duration)
+ out << _("duration: ") << *interval.duration << std::endl;
+
+ if (interval.find_period(interval.start ?
+ *interval.start : CURRENT_DATE())) {
+ out << std::endl
+ << _("after finding first period => ") << std::endl
+ << std::endl;
+
+ if (interval.start)
+ out << _(" start: ") << format_date(*interval.start) << std::endl;
+ if (interval.end)
+ out << _(" end: ") << format_date(*interval.end) << std::endl;
+
+ if (interval.skip_duration)
+ out << _(" skip: ") << *interval.skip_duration << std::endl;
+ if (interval.factor)
+ out << _(" factor: ") << interval.factor << std::endl;
+ if (interval.duration)
+ out << _("duration: ") << *interval.duration << std::endl;
- if (! is_valid(interval.begin)) {
- out << _("Time period has no beginning.") << std::endl;
- } else {
- out << _("begin: ") << format_date(interval.begin) << std::endl;
- out << _(" end: ") << format_date(interval.end) << std::endl;
out << std::endl;
- date_t date = interval.first();
-
- for (int i = 0; i < 20; i++) {
+ for (int i = 0; i < 20 && interval; i++, ++interval) {
out << std::right;
out.width(2);
- out << i << "): " << format_date(date) << std::endl;
+ out << i << "): " << format_date(*interval.start);
+ if (interval.end_of_duration)
+ out << " -- " << format_date(interval.inclusive_end());
+ out << std::endl;
- date = interval.increment(date);
- if (is_valid(interval.end) && date >= interval.end)
+ if (! interval.skip_duration)
break;
}
}
diff --git a/src/report.h b/src/report.h
index 55c37b22..5eb2a706 100644
--- a/src/report.h
+++ b/src/report.h
@@ -240,14 +240,14 @@ public:
});
OPTION_(report_t, begin_, DO_(args) { // -b
- interval_t interval(args[0].to_string());
- if (! is_valid(interval.begin))
+ date_interval_t interval(args[0].to_string());
+ if (! interval.start)
throw_(std::invalid_argument,
_("Could not determine beginning of period '%1'")
<< args[0].to_string());
string predicate =
- "date>=[" + to_iso_extended_string(interval.begin) + "]";
+ "date>=[" + to_iso_extended_string(*interval.start) + "]";
parent->HANDLER(limit_).on(predicate);
});
@@ -355,14 +355,14 @@ public:
OPTION(report_t, empty); // -E
OPTION_(report_t, end_, DO_(args) { // -e
- interval_t interval(args[0].to_string());
- if (! is_valid(interval.begin))
+ date_interval_t interval(args[0].to_string());
+ if (! interval.start)
throw_(std::invalid_argument,
_("Could not determine end of period '%1'")
<< args[0].to_string());
string predicate =
- "date<[" + to_iso_extended_string(interval.begin) + "]";
+ "date<[" + to_iso_extended_string(*interval.start) + "]";
parent->HANDLER(limit_).on(predicate);
#if 0
terminus = interval.begin;
diff --git a/src/times.cc b/src/times.cc
index d83b6baa..512d377b 100644
--- a/src/times.cc
+++ b/src/times.cc
@@ -107,25 +107,25 @@ namespace {
}
}
-int string_to_day_of_week(const std::string& str)
+date_time::weekdays string_to_day_of_week(const std::string& str)
{
if (str == _("sun") || str == _("sunday") || str == "0")
- return 0;
+ return gregorian::Sunday;
else if (str == _("mon") || str == _("monday") || str == "1")
- return 1;
+ return gregorian::Monday;
else if (str == _("tue") || str == _("tuesday") || str == "2")
- return 2;
+ return gregorian::Tuesday;
else if (str == _("wed") || str == _("wednesday") || str == "3")
- return 3;
+ return gregorian::Wednesday;
else if (str == _("thu") || str == _("thursday") || str == "4")
- return 4;
+ return gregorian::Thursday;
else if (str == _("fri") || str == _("friday") || str == "5")
- return 5;
+ return gregorian::Friday;
else if (str == _("sat") || str == _("saturday") || str == "6")
- return 6;
+ return gregorian::Saturday;
assert(false);
- return -1;
+ return gregorian::Sunday;
}
datetime_t parse_datetime(const char * str, int)
@@ -145,50 +145,156 @@ date_t parse_date(const char * str, int current_year)
return gregorian::date_from_tm(when);
}
-date_t interval_t::first(const optional<date_t>& moment)
+date_t date_interval_t::add_duration(const date_t& date,
+ const duration_t& duration)
{
- if (! is_valid(begin)) {
- // Find an efficient starting point for the upcoming while loop. We want
- // a date early enough that the range will be correct, but late enough
- // that we don't spend hundreds of thousands of loops skipping through
- // time.
- assert(moment);
-
- if (months > 0 || years > 0) {
- begin = date_t(moment->year(), gregorian::Jan, 1);
- } else {
- begin = date_t(*moment - gregorian::days(400));
+ if (duration.type() == typeid(gregorian::days))
+ return date + boost::get<gregorian::days>(duration);
+ else if (duration.type() == typeid(gregorian::weeks))
+ return date + boost::get<gregorian::weeks>(duration);
+ else if (duration.type() == typeid(gregorian::months))
+ return date + boost::get<gregorian::months>(duration);
+ else
+ assert(duration.type() == typeid(gregorian::years));
+ return date + boost::get<gregorian::years>(duration);
+}
+
+date_t date_interval_t::subtract_duration(const date_t& date,
+ const duration_t& duration)
+{
+ if (duration.type() == typeid(gregorian::days))
+ return date - boost::get<gregorian::days>(duration);
+ else if (duration.type() == typeid(gregorian::weeks))
+ return date - boost::get<gregorian::weeks>(duration);
+ else if (duration.type() == typeid(gregorian::months))
+ return date - boost::get<gregorian::months>(duration);
+ else
+ assert(duration.type() == typeid(gregorian::years));
+ return date - boost::get<gregorian::years>(duration);
+}
+
+std::ostream& operator<<(std::ostream& out,
+ const date_interval_t::duration_t& duration)
+{
+ if (duration.type() == typeid(gregorian::days))
+ out << boost::get<gregorian::days>(duration).days()
+ << " days";
+ else if (duration.type() == typeid(gregorian::weeks))
+ out << (boost::get<gregorian::weeks>(duration).days() / 7)
+ << " weeks";
+ else if (duration.type() == typeid(gregorian::months))
+ out << boost::get<gregorian::months>(duration).number_of_months()
+ << " months";
+ else {
+ assert(duration.type() == typeid(gregorian::years));
+ out << boost::get<gregorian::years>(duration).number_of_years()
+ << " years";
+ }
+ return out;
+}
+
+bool date_interval_t::find_period(const date_t& date,
+ date_interval_t * last_interval)
+{
+ if (end && date > *end)
+ return false;
+
+ if (! start) {
+ if (duration) {
+ // The interval object has not been seeded with a start date yet, so
+ // find the nearest period before on on date which fits, if possible.
+ //
+ // Find an efficient starting point for the upcoming while loop. We
+ // want a date early enough that the range will be correct, but late
+ // enough that we don't spend hundreds of thousands of loops skipping
+ // through time.
+ if (duration->type() == typeid(gregorian::months) ||
+ duration->type() == typeid(gregorian::weeks)) {
+ start = date_t(date.year(), gregorian::Jan, 1);
+ } else {
+ start = date_t(date - gregorian::days(400));
- // jww (2009-02-21): Add support for starting a week on any day
- if (weekly) { // move it to a Sunday
- while (begin.day_of_week() != start_of_week)
- begin += gregorian::days(1);
+ if (duration->type() == typeid(gregorian::weeks)) {
+ // Move it to a Sunday
+ while (start->day_of_week() != start_of_week)
+ *start += gregorian::days(1);
+ }
}
}
}
- date_t quant(begin);
+ if (date < *start)
+ return false;
+
+ // If there is no duration, then if we've reached here the date falls
+ // between begin and end.
+ if (! duration) {
+ if (! start && ! end)
+ throw_(date_error,
+ _("Invalid date interval: neither start, nor end, nor duration"));
+ return true;
+ }
+
+ if (! end_of_duration)
+ end_of_duration = add_duration(*start, *duration);
+
+ if (! skip_duration)
+ skip_duration = duration;
+
+ if (! next)
+ next = add_duration(*start, *skip_duration);
+
+ if (date < *end_of_duration)
+ return true;
+
+ // If we've reached here, it means the date does not fall into the current
+ // interval, so we must seek another interval that does match -- unless we
+ // pass by date in so doing, which means we shouldn't alter the current
+ // period of the interval at all.
+
+ date_t scan = *next;
+ date_t end_of_scan = add_duration(scan, *duration);
- if (moment && *moment >= quant) {
- date_t temp;
- while (*moment >= (temp = increment(quant))) {
- if (quant == temp)
- break;
- quant = temp;
+ while (date >= scan && (! end || scan < *end)) {
+ if (date < end_of_scan) {
+ if (last_interval) {
+ last_interval->start = start;
+ last_interval->next = next;
+ last_interval->end_of_duration = end_of_duration;
+ }
+ start = scan;
+ end_of_duration = end_of_scan;
+ next = none;
+ return true;
}
+ scan = add_duration(scan, *skip_duration);
+ end_of_scan = add_duration(scan, *duration);
}
- return quant;
+
+ return false;
}
-date_t interval_t::increment(const date_t& moment) const
+date_interval_t& date_interval_t::operator++()
{
- date_t future(moment);
+ if (! start)
+ throw_(date_error, _("Cannot increment an unstarted date interval"));
+
+ if (! skip_duration) {
+ if (duration)
+ skip_duration = duration;
+ else
+ throw_(date_error,
+ _("Cannot increment a date interval without a duration"));
+ }
+
+ *start = add_duration(*start, *skip_duration);
- if (years) future += gregorian::years(years);
- if (months) future += gregorian::months(months);
- if (days) future += gregorian::days(days);
+ if (end && *start >= *end)
+ start = none;
+ else
+ end_of_duration = add_duration(*start, *duration);
- return future;
+ return *this;
}
namespace {
@@ -230,9 +336,15 @@ namespace {
if (begin) {
*begin = gregorian::date_from_tm(when);
- if (end)
- *end = interval_t(saw_day ? 1 : 0, saw_mon ? 1 : 0,
- saw_year ? 1 : 0).increment(*begin);
+
+ if (end) {
+ if (saw_year)
+ *end = *begin + gregorian::years(1);
+ else if (saw_mon)
+ *end = *begin + gregorian::months(1);
+ else if (saw_day)
+ *end = *begin + gregorian::days(1);
+ }
}
else if (end) {
*end = gregorian::date_from_tm(when);
@@ -245,14 +357,14 @@ namespace {
word[i] = static_cast<char>(std::tolower(word[i]));
}
- void parse_date_words(std::istream& in, string& word,
- date_t * begin, date_t * end)
+ void parse_date_words(std::istream& in,
+ string& word,
+ date_interval_t& interval,
+ bool look_for_start = true,
+ bool look_for_end = true)
{
string type;
- bool mon_spec = false;
- char buf[32];
-
if (word == _("this") || word == _("last") || word == _("next")) {
type = word;
if (! in.eof())
@@ -263,59 +375,48 @@ namespace {
type = _("this");
}
- if (word == _("month")) {
- time_t now = to_time_t(CURRENT_TIME());
- std::strftime(buf, 31, "%B", localtime(&now));
- word = buf;
- mon_spec = true;
+ date_t start = CURRENT_DATE();
+ date_t end;
+ bool parse_specifier = false;
+
+ date_interval_t::duration_t duration;
+
+ assert(look_for_start || look_for_end);
+
+ if (word == _("year")) {
+ duration = gregorian::years(1);
+ start = gregorian::date(start.year(), 1, 1);
}
- else if (word == _("year")) {
- int year = CURRENT_DATE().year();
- std::sprintf(buf, "%04d", year);
- word = buf;
+ else if (word == _("month")) {
+ duration = gregorian::months(1);
+ start = gregorian::date(start.year(), start.month(), 1);
}
- else if (word == _("today")) {
- if (begin)
- *begin = CURRENT_DATE();
- if (end) {
- *end = CURRENT_DATE();
- *end += gregorian::days(1);
- }
- return;
+ else if (word == _("today") || word == _("day")) {
+ duration = gregorian::days(1);
}
+ else {
+ parse_specifier = true;
+ }
+ end = date_interval_t::add_duration(start, duration);
- parse_inclusion_specifier(word, begin, end);
+ if (parse_specifier)
+ parse_inclusion_specifier(word, &start, &end);
if (type == _("last")) {
- if (mon_spec) {
- if (begin)
- *begin = interval_t(0, -1, 0).increment(*begin);
- if (end)
- *end = interval_t(0, -1, 0).increment(*end);
- } else {
- if (begin)
- *begin = interval_t(0, 0, -1).increment(*begin);
- if (end)
- *end = interval_t(0, 0, -1).increment(*end);
- }
+ start = date_interval_t::subtract_duration(start, duration);
+ end = date_interval_t::subtract_duration(end, duration);
}
else if (type == _("next")) {
- if (mon_spec) {
- if (begin)
- *begin = interval_t(0, 1, 0).increment(*begin);
- if (end)
- *end = interval_t(0, 1, 0).increment(*end);
- } else {
- if (begin)
- *begin = interval_t(0, 0, 1).increment(*begin);
- if (end)
- *end = interval_t(0, 0, 1).increment(*end);
- }
+ start = date_interval_t::add_duration(start, duration);
+ end = date_interval_t::add_duration(end, duration);
}
+
+ if (look_for_start) interval.start = start;
+ if (look_for_end) interval.end = end;
}
}
-void interval_t::parse(std::istream& in)
+void date_interval_t::parse(std::istream& in)
{
string word;
@@ -327,65 +428,62 @@ void interval_t::parse(std::istream& in)
int quantity = lexical_cast<int>(word);
read_lower_word(in, word);
if (word == _("days"))
- days = quantity;
- else if (word == _("weeks")) {
- days = 7 * quantity;
- weekly = true;
- }
+ duration = gregorian::days(quantity);
+ else if (word == _("weeks"))
+ duration = gregorian::weeks(quantity);
else if (word == _("months"))
- months = quantity;
+ duration = gregorian::months(quantity);
else if (word == _("quarters"))
- months = 3 * quantity;
+ duration = gregorian::months(3 * quantity);
else if (word == _("years"))
- years = quantity;
+ duration = gregorian::years(quantity);
}
else if (word == _("day"))
- days = 1;
- else if (word == _("week")) {
- days = 7;
- weekly = true;
- }
+ duration = gregorian::days(1);
+ else if (word == _("week"))
+ duration = gregorian::weeks(1);
else if (word == _("month"))
- months = 1;
+ duration = gregorian::months(1);
else if (word == _("quarter"))
- months = 3;
+ duration = gregorian::months(3);
else if (word == _("year"))
- years = 1;
+ duration = gregorian::years(1);
}
else if (word == _("daily"))
- days = 1;
- else if (word == _("weekly")) {
- days = 7;
- weekly = true;
- }
+ duration = gregorian::days(1);
+ else if (word == _("weekly"))
+ duration = gregorian::weeks(1);
else if (word == _("biweekly"))
- days = 14;
+ duration = gregorian::weeks(2);
else if (word == _("monthly"))
- months = 1;
+ duration = gregorian::months(1);
else if (word == _("bimonthly"))
- months = 2;
+ duration = gregorian::months(2);
else if (word == _("quarterly"))
- months = 3;
+ duration = gregorian::months(3);
else if (word == _("yearly"))
- years = 1;
+ duration = gregorian::years(1);
else if (word == _("this") || word == _("last") || word == _("next") ||
word == _("today")) {
- parse_date_words(in, word, &begin, &end);
+ parse_date_words(in, word, *this);
}
else if (word == _("in")) {
read_lower_word(in, word);
- parse_date_words(in, word, &begin, &end);
+ parse_date_words(in, word, *this);
}
else if (word == _("from") || word == _("since")) {
read_lower_word(in, word);
- parse_date_words(in, word, &begin, NULL);
+ parse_date_words(in, word, *this, true, false);
}
else if (word == _("to") || word == _("until")) {
read_lower_word(in, word);
- parse_date_words(in, word, NULL, &end);
+ parse_date_words(in, word, *this, false, true);
}
else {
- parse_inclusion_specifier(word, &begin, &end);
+ date_t b, e;
+ parse_inclusion_specifier(word, &b, &e);
+ start = b;
+ end = e;
}
}
}
diff --git a/src/times.h b/src/times.h
index 5a40361e..3ed3621d 100644
--- a/src/times.h
+++ b/src/times.h
@@ -60,6 +60,7 @@ inline bool is_valid(const datetime_t& moment) {
typedef boost::gregorian::date date_t;
typedef boost::gregorian::date_duration date_duration_t;
+typedef boost::gregorian::date_iterator date_iterator_t;
inline bool is_valid(const date_t& moment) {
return ! moment.is_not_a_date();
@@ -75,7 +76,7 @@ inline bool is_valid(const date_t& moment) {
extern int start_of_week;
extern optional<std::string> input_date_format;
-int string_to_day_of_week(const std::string& str);
+date_time::weekdays string_to_day_of_week(const std::string& str);
datetime_t parse_datetime(const char * str, int current_year = -1);
@@ -123,94 +124,83 @@ inline std::string format_date(const date_t& when,
return buf;
}
-/**
- * @brief Brief
- *
- * Long.
- */
-struct range_t
+class date_interval_t : public equality_comparable<date_interval_t>
{
- date_t begin;
- date_t end;
-
- date_range_t(const date_t& _begin = date_t(),
- const date_t& _end = date_t())
- : begin(_begin), end(_end) {
- TRACE_CTOR(date_range_t, "const date_t&, const date_t&");
+public:
+ typedef variant<gregorian::days,
+ gregorian::weeks,
+ gregorian::months,
+ gregorian::years> duration_t;
+
+ static date_t add_duration(const date_t& date,
+ const duration_t& duration);
+ static date_t subtract_duration(const date_t& date,
+ const duration_t& duration);
+
+ optional<date_t> start;
+ optional<duration_t> skip_duration;
+ std::size_t factor;
+ optional<date_t> next;
+ optional<duration_t> duration;
+ optional<date_t> end_of_duration;
+ optional<date_t> end;
+
+ explicit date_interval_t() : factor(1) {
+ TRACE_CTOR(date_interval_t, "");
+ }
+ date_interval_t(const string& str) : factor(1) {
+ TRACE_CTOR(date_interval_t, "const string&");
+ parse(str);
}
- date_range_t(const date_range_t& other)
- : begin(other.begin), end(other.end) {
- TRACE_CTOR(date_range_t, "copy");
+ date_interval_t(const date_interval_t& other)
+ : start(other.start),
+ skip_duration(other.skip_duration),
+ factor(other.factor),
+ next(other.next),
+ duration(other.duration),
+ end_of_duration(other.end_of_duration),
+ end(other.end) {
+ TRACE_CTOR(date_interval_t, "copy");
}
- date_range_t(const string& desc) : begin(), end() {
- TRACE_CTOR(date_range_t, "const string&");
- std::istringstream stream(desc);
- parse(stream);
+ ~date_interval_t() throw() {
+ TRACE_DTOR(date_interval_t);
}
- ~date_range_t() throw() {
- TRACE_DTOR(date_range_t);
+
+ bool operator==(const date_interval_t& other) const {
+ return (start == other.start &&
+ (! start || *start == *other.start));
}
- bool date_in_range(const date_t& date) {
- return ((! is_valid(begin) || date >= begin) &&
- (! is_valid(end) || date < end));
+ operator bool() const {
+ return is_valid();
}
void parse(std::istream& in);
-};
-/**
- * @brief Brief
- *
- * Long.
- */
-struct interval_t
-{
- int years;
- int months;
- int days;
- bool weekly;
-
- interval_t(int _days = 0,
- int _months = 0,
- int _years = 0,
- bool _weekly = false)
- : years(_years), months(_months), days(_days), weekly(_weekly) {
- TRACE_CTOR(interval_t, "int, int, int, bool");
- }
- interval_t(const interval_t& other)
- : years(other.years),
- months(other.months),
- days(other.days),
- weekly(other.weekly) {
- TRACE_CTOR(interval_t, "copy");
- }
- interval_t(const string& desc)
- : years(0), months(0), days(0), weekly(false) {
- TRACE_CTOR(interval_t, "const string&");
- std::istringstream stream(desc);
- parse(stream);
- }
- ~interval_t() throw() {
- TRACE_DTOR(interval_t);
+ void parse(const string& str) {
+ std::istringstream in(str);
+ parse(in);
}
- operator bool() const {
- return years != 0 || months != 0 || days != 0;
+ bool is_valid() const {
+ return start;
}
-#if 0
- void set_start(const date_t& moment) {
- begin = first(moment);
- }
-#endif
+ /** Find the current or next period containing date. Returns true if the
+ date_interval_t object has been altered to reflect the interval
+ containing date, or false if no such period can be found. */
+ bool find_period(const date_t& date, date_interval_t * last_interval = NULL);
- date_t first(const optional<date_t>& moment = none);
- date_t increment(const date_t&) const;
+ date_t inclusive_end() const {
+ return *end_of_duration - gregorian::days(1);
+ }
- void parse(std::istream& in);
+ date_interval_t& operator++();
};
+std::ostream& operator<<(std::ostream& out,
+ const date_interval_t::duration_t& duration);
+
} // namespace ledger
#endif // _TIMES_H
diff --git a/src/token.cc b/src/token.cc
index b7c19ceb..fde6a0e3 100644
--- a/src/token.cc
+++ b/src/token.cc
@@ -205,9 +205,12 @@ void expr_t::token_t::next(std::istream& in, const uint_least8_t pflags)
in.get(c);
length++;
- interval_t timespan(buf);
+ date_interval_t timespan(buf);
+ if (! timespan)
+ throw_(parse_error,
+ _("Date specifier does not refer to a starting date"));
kind = VALUE;
- value = timespan.first();
+ value = *timespan.start;
break;
}
diff --git a/src/xact.h b/src/xact.h
index 6f35a071..174a8cea 100644
--- a/src/xact.h
+++ b/src/xact.h
@@ -186,8 +186,8 @@ struct auto_xact_finalizer_t : public xact_finalizer_t
class period_xact_t : public xact_base_t
{
public:
- interval_t period;
- string period_string;
+ date_interval_t period;
+ string period_string;
period_xact_t() {
TRACE_CTOR(period_xact_t, "");
@@ -206,7 +206,7 @@ class period_xact_t : public xact_base_t
}
virtual bool valid() const {
- return period;
+ return period.is_valid();
}
};