diff options
-rw-r--r-- | src/filters.cc | 76 | ||||
-rw-r--r-- | src/filters.h | 40 | ||||
-rw-r--r-- | src/precmd.cc | 53 | ||||
-rw-r--r-- | src/report.h | 12 | ||||
-rw-r--r-- | src/times.cc | 336 | ||||
-rw-r--r-- | src/times.h | 132 | ||||
-rw-r--r-- | src/token.cc | 7 | ||||
-rw-r--r-- | src/xact.h | 6 |
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; } @@ -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(); } }; |