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)
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);
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);
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);
last_post = &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);
} 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();
}

View file

@ -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();
};

View file

@ -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;
}
}

View file

@ -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;

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")
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 (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);
}
if (months > 0 || years > 0) {
begin = date_t(moment->year(), gregorian::Jan, 1);
} else {
begin = date_t(*moment - gregorian::days(400));
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);
}
// 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);
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));
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) {
date_t temp;
while (*moment >= (temp = increment(quant))) {
if (quant == temp)
break;
quant = temp;
}
// 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;
}
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 (months) future += gregorian::months(months);
if (days) future += gregorian::days(days);
if (! skip_duration) {
if (duration)
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 {
@ -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;
}
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;
}
date_t start = CURRENT_DATE();
date_t end;
bool parse_specifier = false;
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 (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;
}
}
}

View file

@ -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;
public:
typedef variant<gregorian::days,
gregorian::weeks,
gregorian::months,
gregorian::years> duration_t;
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&");
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_range_t(const date_range_t& other)
: begin(other.begin), end(other.end) {
TRACE_CTOR(date_range_t, "copy");
date_interval_t(const string& str) : factor(1) {
TRACE_CTOR(date_interval_t, "const string&");
parse(str);
}
date_range_t(const string& desc) : begin(), end() {
TRACE_CTOR(date_range_t, "const string&");
std::istringstream stream(desc);
parse(stream);
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() throw() {
TRACE_DTOR(date_range_t);
~date_interval_t() throw() {
TRACE_DTOR(date_interval_t);
}
bool date_in_range(const date_t& date) {
return ((! is_valid(begin) || date >= begin) &&
(! is_valid(end) || date < end));
}
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);
bool operator==(const date_interval_t& other) const {
return (start == other.start &&
(! start || *start == *other.start));
}
operator bool() const {
return years != 0 || months != 0 || days != 0;
return is_valid();
}
#if 0
void set_start(const date_t& moment) {
begin = first(moment);
void parse(std::istream& in);
void parse(const string& str) {
std::istringstream in(str);
parse(in);
}
#endif
date_t first(const optional<date_t>& moment = none);
date_t increment(const date_t&) const;
bool is_valid() 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
#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);
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;
}

View file

@ -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();
}
};