First iteration of the new date_interval_t rewrite

This commit is contained in:
John Wiegley 2009-03-15 01:14:13 -04:00
parent 1889d449b6
commit a05353e269
8 changed files with 390 additions and 274 deletions

View file

@ -562,7 +562,7 @@ void interval_posts::report_subtotal(const date_t& finish)
if (exact_periods) if (exact_periods)
subtotal_posts::report_subtotal(); subtotal_posts::report_subtotal();
else else
subtotal_posts::report_subtotal(NULL, interval.begin, finish); subtotal_posts::report_subtotal(NULL, *interval.start, finish);
} }
last_post = NULL; last_post = NULL;
@ -572,49 +572,37 @@ void interval_posts::operator()(post_t& post)
{ {
date_t date = post.date(); date_t date = post.date();
if ((is_valid(interval.begin) && date < interval.begin) || if (! interval.find_period(post.date(), &last_interval))
(is_valid(interval.end) && date >= interval.end))
return; return;
if (interval) { if (interval.duration) {
if (! is_valid(interval.begin)) if (last_interval) {
interval.set_start(date); if (interval != last_interval) {
start = interval.begin; report_subtotal(last_interval.inclusive_end());
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 (generate_empty_posts) { if (generate_empty_posts) {
// Generate a null posting, so the intervening periods can be for (++last_interval; interval != last_interval; ++last_interval) {
// seen when -E is used, or if the calculated amount ends up being // Generate a null posting, so the intervening periods can be
// non-zero // seen when -E is used, or if the calculated amount ends up being
xact_temps.push_back(xact_t()); // non-zero
xact_t& null_xact = xact_temps.back(); xact_temps.push_back(xact_t());
null_xact.add_flags(ITEM_TEMP); xact_t& null_xact = xact_temps.back();
null_xact._date = quant - gregorian::days(1); null_xact.add_flags(ITEM_TEMP);
null_xact._date = last_interval.inclusive_end();
post_temps.push_back(post_t(&empty_account)); post_temps.push_back(post_t(&empty_account));
post_t& null_post = post_temps.back(); post_t& null_post = post_temps.back();
null_post.add_flags(ITEM_TEMP | POST_CALCULATED); null_post.add_flags(ITEM_TEMP | POST_CALCULATED);
null_post.amount = 0L; null_post.amount = 0L;
null_xact.add_post(&null_post); null_xact.add_post(&null_post);
last_post = &null_post; last_post = &null_post;
subtotal_posts::operator()(null_post); subtotal_posts::operator()(null_post);
report_subtotal(quant - gregorian::days(1)); report_subtotal(last_interval.inclusive_end());
}
} }
} }
start = interval.begin = quant;
} }
subtotal_posts::operator()(post); subtotal_posts::operator()(post);
} else { } else {
@ -745,7 +733,7 @@ void generate_posts::add_period_xacts(period_xacts_list& period_xacts)
add_post(xact->period, *post); 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)); pending_posts.push_back(pending_posts_pair(period, &post));
} }
@ -759,6 +747,7 @@ void budget_posts::report_budget_items(const date_t& date)
do { do {
reported = false; reported = false;
foreach (pending_posts_list::value_type& pair, pending_posts) { foreach (pending_posts_list::value_type& pair, pending_posts) {
#if 0
date_t& begin = pair.first.begin; date_t& begin = pair.first.begin;
if (! is_valid(begin)) { if (! is_valid(begin)) {
pair.first.set_start(date); pair.first.set_start(date);
@ -790,6 +779,7 @@ void budget_posts::report_budget_items(const date_t& date)
reported = true; reported = true;
} }
#endif
} }
} while (reported); } 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); 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)) { if (! is_valid(i.begin)) {
i.set_start(CURRENT_DATE()); i.set_start(CURRENT_DATE());
i.begin = i.increment(i.begin); 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()) while (i.begin < CURRENT_DATE())
i.begin = i.increment(i.begin); i.begin = i.increment(i.begin);
} }
#endif
} }
void forecast_posts::flush() void forecast_posts::flush()
@ -842,6 +834,7 @@ void forecast_posts::flush()
posts_list passed; posts_list passed;
date_t last; date_t last;
#if 0
while (pending_posts.size() > 0) { while (pending_posts.size() > 0) {
pending_posts_list::iterator least = pending_posts.begin(); pending_posts_list::iterator least = pending_posts.begin();
for (pending_posts_list::iterator i = ++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(); item_handler<post_t>::flush();
} }

View file

@ -558,22 +558,22 @@ public:
*/ */
class interval_posts : public subtotal_posts class interval_posts : public subtotal_posts
{ {
interval_t interval; date_interval_t interval;
post_t * last_post; date_interval_t last_interval;
account_t empty_account; post_t * last_post;
bool exact_periods; account_t empty_account;
bool generate_empty_posts; bool exact_periods;
date_t start; bool generate_empty_posts;
interval_posts(); interval_posts();
public: public:
interval_posts(post_handler_ptr _handler, interval_posts(post_handler_ptr _handler,
expr_t& amount_expr, expr_t& amount_expr,
const interval_t& _interval, const date_interval_t& _interval,
bool _exact_periods = false, bool _exact_periods = false,
bool _generate_empty_posts = false) bool _generate_empty_posts = false)
: subtotal_posts(_handler, amount_expr), interval(_interval), : subtotal_posts(_handler, amount_expr), interval(_interval),
last_post(NULL), empty_account(NULL, _("<None>")), last_post(NULL), empty_account(NULL, _("<None>")),
exact_periods(_exact_periods), exact_periods(_exact_periods),
@ -588,9 +588,11 @@ public:
void report_subtotal(const date_t& finish); void report_subtotal(const date_t& finish);
virtual void flush() { virtual void flush() {
if (last_post && interval) if (last_post && interval.duration) {
report_subtotal(interval.increment(interval.begin) - gregorian::days(1)); if (interval.is_valid())
subtotal_posts::flush(); report_subtotal(interval.inclusive_end());
subtotal_posts::flush();
}
} }
virtual void operator()(post_t& post); virtual void operator()(post_t& post);
}; };
@ -725,11 +727,11 @@ class generate_posts : public item_handler<post_t>
generate_posts(); generate_posts();
protected: protected:
typedef std::pair<interval_t, post_t *> pending_posts_pair; typedef std::pair<date_interval_t, post_t *> pending_posts_pair;
typedef std::list<pending_posts_pair> pending_posts_list; typedef std::list<pending_posts_pair> pending_posts_list;
pending_posts_list pending_posts; pending_posts_list pending_posts;
std::list<xact_t> xact_temps; std::list<xact_t> xact_temps;
std::list<post_t> post_temps; std::list<post_t> post_temps;
public: public:
@ -745,7 +747,7 @@ public:
void add_period_xacts(period_xacts_list& period_xacts); 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); 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(); virtual void flush();
}; };

View file

@ -160,25 +160,54 @@ value_t period_command(call_scope_t& args)
report_t& report(find_scope<report_t>(args)); report_t& report(find_scope<report_t>(args));
std::ostream& out(report.output_stream); 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; out << std::endl;
date_t date = interval.first(); for (int i = 0; i < 20 && interval; i++, ++interval) {
for (int i = 0; i < 20; i++) {
out << std::right; out << std::right;
out.width(2); 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 (! interval.skip_duration)
if (is_valid(interval.end) && date >= interval.end)
break; break;
} }
} }

View file

@ -240,14 +240,14 @@ public:
}); });
OPTION_(report_t, begin_, DO_(args) { // -b OPTION_(report_t, begin_, DO_(args) { // -b
interval_t interval(args[0].to_string()); date_interval_t interval(args[0].to_string());
if (! is_valid(interval.begin)) if (! interval.start)
throw_(std::invalid_argument, throw_(std::invalid_argument,
_("Could not determine beginning of period '%1'") _("Could not determine beginning of period '%1'")
<< args[0].to_string()); << args[0].to_string());
string predicate = string predicate =
"date>=[" + to_iso_extended_string(interval.begin) + "]"; "date>=[" + to_iso_extended_string(*interval.start) + "]";
parent->HANDLER(limit_).on(predicate); parent->HANDLER(limit_).on(predicate);
}); });
@ -355,14 +355,14 @@ public:
OPTION(report_t, empty); // -E OPTION(report_t, empty); // -E
OPTION_(report_t, end_, DO_(args) { // -e OPTION_(report_t, end_, DO_(args) { // -e
interval_t interval(args[0].to_string()); date_interval_t interval(args[0].to_string());
if (! is_valid(interval.begin)) if (! interval.start)
throw_(std::invalid_argument, throw_(std::invalid_argument,
_("Could not determine end of period '%1'") _("Could not determine end of period '%1'")
<< args[0].to_string()); << args[0].to_string());
string predicate = string predicate =
"date<[" + to_iso_extended_string(interval.begin) + "]"; "date<[" + to_iso_extended_string(*interval.start) + "]";
parent->HANDLER(limit_).on(predicate); parent->HANDLER(limit_).on(predicate);
#if 0 #if 0
terminus = interval.begin; terminus = interval.begin;

View file

@ -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") if (str == _("sun") || str == _("sunday") || str == "0")
return 0; return gregorian::Sunday;
else if (str == _("mon") || str == _("monday") || str == "1") else if (str == _("mon") || str == _("monday") || str == "1")
return 1; return gregorian::Monday;
else if (str == _("tue") || str == _("tuesday") || str == "2") else if (str == _("tue") || str == _("tuesday") || str == "2")
return 2; return gregorian::Tuesday;
else if (str == _("wed") || str == _("wednesday") || str == "3") else if (str == _("wed") || str == _("wednesday") || str == "3")
return 3; return gregorian::Wednesday;
else if (str == _("thu") || str == _("thursday") || str == "4") else if (str == _("thu") || str == _("thursday") || str == "4")
return 4; return gregorian::Thursday;
else if (str == _("fri") || str == _("friday") || str == "5") else if (str == _("fri") || str == _("friday") || str == "5")
return 5; return gregorian::Friday;
else if (str == _("sat") || str == _("saturday") || str == "6") else if (str == _("sat") || str == _("saturday") || str == "6")
return 6; return gregorian::Saturday;
assert(false); assert(false);
return -1; return gregorian::Sunday;
} }
datetime_t parse_datetime(const char * str, int) 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); 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)) { if (duration.type() == typeid(gregorian::days))
// Find an efficient starting point for the upcoming while loop. We want return date + boost::get<gregorian::days>(duration);
// a date early enough that the range will be correct, but late enough else if (duration.type() == typeid(gregorian::weeks))
// that we don't spend hundreds of thousands of loops skipping through return date + boost::get<gregorian::weeks>(duration);
// time. else if (duration.type() == typeid(gregorian::months))
assert(moment); return date + boost::get<gregorian::months>(duration);
else
assert(duration.type() == typeid(gregorian::years));
return date + boost::get<gregorian::years>(duration);
}
if (months > 0 || years > 0) { date_t date_interval_t::subtract_duration(const date_t& date,
begin = date_t(moment->year(), gregorian::Jan, 1); const duration_t& duration)
} 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);
}
// jww (2009-02-21): Add support for starting a week on any day std::ostream& operator<<(std::ostream& out,
if (weekly) { // move it to a Sunday const date_interval_t::duration_t& duration)
while (begin.day_of_week() != start_of_week) {
begin += gregorian::days(1); 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));
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 (moment && *moment >= quant) { // If there is no duration, then if we've reached here the date falls
date_t temp; // between begin and end.
while (*moment >= (temp = increment(quant))) { if (! duration) {
if (quant == temp) if (! start && ! end)
break; throw_(date_error,
quant = temp; _("Invalid date interval: neither start, nor end, nor duration"));
} return true;
} }
return quant;
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);
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 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 (years) future += gregorian::years(years); if (! skip_duration) {
if (months) future += gregorian::months(months); if (duration)
if (days) future += gregorian::days(days); skip_duration = duration;
else
throw_(date_error,
_("Cannot increment a date interval without a duration"));
}
return future; *start = add_duration(*start, *skip_duration);
if (end && *start >= *end)
start = none;
else
end_of_duration = add_duration(*start, *duration);
return *this;
} }
namespace { namespace {
@ -230,9 +336,15 @@ namespace {
if (begin) { if (begin) {
*begin = gregorian::date_from_tm(when); *begin = gregorian::date_from_tm(when);
if (end)
*end = interval_t(saw_day ? 1 : 0, saw_mon ? 1 : 0, if (end) {
saw_year ? 1 : 0).increment(*begin); 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) { else if (end) {
*end = gregorian::date_from_tm(when); *end = gregorian::date_from_tm(when);
@ -245,14 +357,14 @@ namespace {
word[i] = static_cast<char>(std::tolower(word[i])); word[i] = static_cast<char>(std::tolower(word[i]));
} }
void parse_date_words(std::istream& in, string& word, void parse_date_words(std::istream& in,
date_t * begin, date_t * end) string& word,
date_interval_t& interval,
bool look_for_start = true,
bool look_for_end = true)
{ {
string type; string type;
bool mon_spec = false;
char buf[32];
if (word == _("this") || word == _("last") || word == _("next")) { if (word == _("this") || word == _("last") || word == _("next")) {
type = word; type = word;
if (! in.eof()) if (! in.eof())
@ -263,59 +375,48 @@ namespace {
type = _("this"); type = _("this");
} }
if (word == _("month")) { date_t start = CURRENT_DATE();
time_t now = to_time_t(CURRENT_TIME()); date_t end;
std::strftime(buf, 31, "%B", localtime(&now)); bool parse_specifier = false;
word = buf;
mon_spec = true;
}
else if (word == _("year")) {
int year = CURRENT_DATE().year();
std::sprintf(buf, "%04d", year);
word = buf;
}
else if (word == _("today")) {
if (begin)
*begin = CURRENT_DATE();
if (end) {
*end = CURRENT_DATE();
*end += gregorian::days(1);
}
return;
}
parse_inclusion_specifier(word, begin, end); 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 == _("month")) {
duration = gregorian::months(1);
start = gregorian::date(start.year(), start.month(), 1);
}
else if (word == _("today") || word == _("day")) {
duration = gregorian::days(1);
}
else {
parse_specifier = true;
}
end = date_interval_t::add_duration(start, duration);
if (parse_specifier)
parse_inclusion_specifier(word, &start, &end);
if (type == _("last")) { if (type == _("last")) {
if (mon_spec) { start = date_interval_t::subtract_duration(start, duration);
if (begin) end = date_interval_t::subtract_duration(end, duration);
*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);
}
} }
else if (type == _("next")) { else if (type == _("next")) {
if (mon_spec) { start = date_interval_t::add_duration(start, duration);
if (begin) end = date_interval_t::add_duration(end, duration);
*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);
}
} }
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; string word;
@ -327,65 +428,62 @@ void interval_t::parse(std::istream& in)
int quantity = lexical_cast<int>(word); int quantity = lexical_cast<int>(word);
read_lower_word(in, word); read_lower_word(in, word);
if (word == _("days")) if (word == _("days"))
days = quantity; duration = gregorian::days(quantity);
else if (word == _("weeks")) { else if (word == _("weeks"))
days = 7 * quantity; duration = gregorian::weeks(quantity);
weekly = true;
}
else if (word == _("months")) else if (word == _("months"))
months = quantity; duration = gregorian::months(quantity);
else if (word == _("quarters")) else if (word == _("quarters"))
months = 3 * quantity; duration = gregorian::months(3 * quantity);
else if (word == _("years")) else if (word == _("years"))
years = quantity; duration = gregorian::years(quantity);
} }
else if (word == _("day")) else if (word == _("day"))
days = 1; duration = gregorian::days(1);
else if (word == _("week")) { else if (word == _("week"))
days = 7; duration = gregorian::weeks(1);
weekly = true;
}
else if (word == _("month")) else if (word == _("month"))
months = 1; duration = gregorian::months(1);
else if (word == _("quarter")) else if (word == _("quarter"))
months = 3; duration = gregorian::months(3);
else if (word == _("year")) else if (word == _("year"))
years = 1; duration = gregorian::years(1);
} }
else if (word == _("daily")) else if (word == _("daily"))
days = 1; duration = gregorian::days(1);
else if (word == _("weekly")) { else if (word == _("weekly"))
days = 7; duration = gregorian::weeks(1);
weekly = true;
}
else if (word == _("biweekly")) else if (word == _("biweekly"))
days = 14; duration = gregorian::weeks(2);
else if (word == _("monthly")) else if (word == _("monthly"))
months = 1; duration = gregorian::months(1);
else if (word == _("bimonthly")) else if (word == _("bimonthly"))
months = 2; duration = gregorian::months(2);
else if (word == _("quarterly")) else if (word == _("quarterly"))
months = 3; duration = gregorian::months(3);
else if (word == _("yearly")) else if (word == _("yearly"))
years = 1; duration = gregorian::years(1);
else if (word == _("this") || word == _("last") || word == _("next") || else if (word == _("this") || word == _("last") || word == _("next") ||
word == _("today")) { word == _("today")) {
parse_date_words(in, word, &begin, &end); parse_date_words(in, word, *this);
} }
else if (word == _("in")) { else if (word == _("in")) {
read_lower_word(in, word); read_lower_word(in, word);
parse_date_words(in, word, &begin, &end); parse_date_words(in, word, *this);
} }
else if (word == _("from") || word == _("since")) { else if (word == _("from") || word == _("since")) {
read_lower_word(in, word); 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")) { else if (word == _("to") || word == _("until")) {
read_lower_word(in, word); read_lower_word(in, word);
parse_date_words(in, word, NULL, &end); parse_date_words(in, word, *this, false, true);
} }
else { else {
parse_inclusion_specifier(word, &begin, &end); date_t b, e;
parse_inclusion_specifier(word, &b, &e);
start = b;
end = e;
} }
} }
} }

View file

@ -60,6 +60,7 @@ inline bool is_valid(const datetime_t& moment) {
typedef boost::gregorian::date date_t; typedef boost::gregorian::date date_t;
typedef boost::gregorian::date_duration date_duration_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) { inline bool is_valid(const date_t& moment) {
return ! moment.is_not_a_date(); return ! moment.is_not_a_date();
@ -75,7 +76,7 @@ inline bool is_valid(const date_t& moment) {
extern int start_of_week; extern int start_of_week;
extern optional<std::string> input_date_format; 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); 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; return buf;
} }
/** class date_interval_t : public equality_comparable<date_interval_t>
* @brief Brief
*
* Long.
*/
struct range_t
{ {
date_t begin; public:
date_t end; typedef variant<gregorian::days,
gregorian::weeks,
gregorian::months,
gregorian::years> duration_t;
date_range_t(const date_t& _begin = date_t(), static date_t add_duration(const date_t& date,
const date_t& _end = date_t()) const duration_t& duration);
: begin(_begin), end(_end) { static date_t subtract_duration(const date_t& date,
TRACE_CTOR(date_range_t, "const date_t&, const date_t&"); 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_range_t(const date_range_t& other) date_interval_t(const string& str) : factor(1) {
: begin(other.begin), end(other.end) { TRACE_CTOR(date_interval_t, "const string&");
TRACE_CTOR(date_range_t, "copy"); parse(str);
} }
date_range_t(const string& desc) : begin(), end() { date_interval_t(const date_interval_t& other)
TRACE_CTOR(date_range_t, "const string&"); : start(other.start),
std::istringstream stream(desc); skip_duration(other.skip_duration),
parse(stream); 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() throw() { ~date_interval_t() throw() {
TRACE_DTOR(date_range_t); TRACE_DTOR(date_interval_t);
} }
bool date_in_range(const date_t& date) { bool operator==(const date_interval_t& other) const {
return ((! is_valid(begin) || date >= begin) && return (start == other.start &&
(! is_valid(end) || date < end)); (! start || *start == *other.start));
}
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);
} }
operator bool() const { operator bool() const {
return years != 0 || months != 0 || days != 0; return is_valid();
} }
#if 0 void parse(std::istream& in);
void set_start(const date_t& moment) {
begin = first(moment); void parse(const string& str) {
std::istringstream in(str);
parse(in);
} }
#endif
date_t first(const optional<date_t>& moment = none); bool is_valid() const {
date_t increment(const date_t&) const; return start;
}
void parse(std::istream& in); /** 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 inclusive_end() const {
return *end_of_duration - gregorian::days(1);
}
date_interval_t& operator++();
}; };
std::ostream& operator<<(std::ostream& out,
const date_interval_t::duration_t& duration);
} // namespace ledger } // namespace ledger
#endif // _TIMES_H #endif // _TIMES_H

View file

@ -205,9 +205,12 @@ void expr_t::token_t::next(std::istream& in, const uint_least8_t pflags)
in.get(c); in.get(c);
length++; 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; kind = VALUE;
value = timespan.first(); value = *timespan.start;
break; break;
} }

View file

@ -186,8 +186,8 @@ struct auto_xact_finalizer_t : public xact_finalizer_t
class period_xact_t : public xact_base_t class period_xact_t : public xact_base_t
{ {
public: public:
interval_t period; date_interval_t period;
string period_string; string period_string;
period_xact_t() { period_xact_t() {
TRACE_CTOR(period_xact_t, ""); TRACE_CTOR(period_xact_t, "");
@ -206,7 +206,7 @@ class period_xact_t : public xact_base_t
} }
virtual bool valid() const { virtual bool valid() const {
return period; return period.is_valid();
} }
}; };