Added feature to "align" the interval's start date

This commit is contained in:
John Wiegley 2009-03-15 04:03:17 -04:00
parent a05353e269
commit 585b3a246d
2 changed files with 149 additions and 42 deletions

View file

@ -178,29 +178,56 @@ std::ostream& operator<<(std::ostream& out,
{ {
if (duration.type() == typeid(gregorian::days)) if (duration.type() == typeid(gregorian::days))
out << boost::get<gregorian::days>(duration).days() out << boost::get<gregorian::days>(duration).days()
<< " days"; << " day(s)";
else if (duration.type() == typeid(gregorian::weeks)) else if (duration.type() == typeid(gregorian::weeks))
out << (boost::get<gregorian::weeks>(duration).days() / 7) out << (boost::get<gregorian::weeks>(duration).days() / 7)
<< " weeks"; << " week(s)";
else if (duration.type() == typeid(gregorian::months)) else if (duration.type() == typeid(gregorian::months))
out << boost::get<gregorian::months>(duration).number_of_months() out << boost::get<gregorian::months>(duration).number_of_months()
<< " months"; << " month(s)";
else { else {
assert(duration.type() == typeid(gregorian::years)); assert(duration.type() == typeid(gregorian::years));
out << boost::get<gregorian::years>(duration).number_of_years() out << boost::get<gregorian::years>(duration).number_of_years()
<< " years"; << " year(s)";
} }
return out; return out;
} }
bool date_interval_t::find_period(const date_t& date, void date_interval_t::resolve_end()
date_interval_t * last_interval)
{ {
if (end && date > *end) if (! end_of_duration) {
return false; end_of_duration = add_duration(*start, *duration);
DEBUG("times.interval",
"stabilize: end_of_duration = " << *end_of_duration);
}
if (! start) { if (end && *end_of_duration > *end) {
end_of_duration = end;
DEBUG("times.interval",
"stabilize: end_of_duration reset to end: " << *end_of_duration);
}
if (! skip_duration) {
skip_duration = duration;
DEBUG("times.interval",
"stabilize: skip_duration set to: " << *skip_duration);
}
if (start && ! next) {
next = add_duration(*start, *skip_duration);
DEBUG("times.interval",
"stabilize: next set to: " << *next);
}
}
void date_interval_t::stabilize(const optional<date_t>& date)
{
if (date && ! aligned) {
DEBUG("times.interval", "stabilize: date passed, but not aligned");
if (duration) { if (duration) {
DEBUG("times.interval",
"stabilize: aligning with a duration: " << *duration);
// The interval object has not been seeded with a start date yet, so // 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 the nearest period before on on date which fits, if possible.
// //
@ -208,11 +235,29 @@ bool date_interval_t::find_period(const date_t& date,
// want a date early enough that the range will be correct, but late // 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 // enough that we don't spend hundreds of thousands of loops skipping
// through time. // through time.
optional<date_t> initial_start = start;
optional<date_t> initial_end = end;
#if defined(DEBUG_ON)
if (initial_start)
DEBUG("times.interval",
"stabilize: initial_start = " << *initial_start);
if (initial_end)
DEBUG("times.interval",
"stabilize: initial_end = " << *initial_end);
#endif
date_t when = start ? *start : *date;
if (duration->type() == typeid(gregorian::months) || if (duration->type() == typeid(gregorian::months) ||
duration->type() == typeid(gregorian::weeks)) { duration->type() == typeid(gregorian::years)) {
start = date_t(date.year(), gregorian::Jan, 1); DEBUG("times.interval", "stabilize: monthly or yearly duration");
start = date_t(when.year(), gregorian::Jan, 1);
} else { } else {
start = date_t(date - gregorian::days(400)); DEBUG("times.interval", "stabilize: daily or weekly duration");
start = date_t(when - gregorian::days(400));
if (duration->type() == typeid(gregorian::weeks)) { if (duration->type() == typeid(gregorian::weeks)) {
// Move it to a Sunday // Move it to a Sunday
@ -220,40 +265,87 @@ bool date_interval_t::find_period(const date_t& date,
*start += gregorian::days(1); *start += gregorian::days(1);
} }
} }
}
}
if (date < *start) DEBUG("times.interval",
return false; "stabilize: beginning start date = " << *start);
while (*start < *date) {
date_interval_t next_interval(*this);
++next_interval;
if (next_interval.start && *next_interval.start < *date) {
*this = next_interval;
} else {
end_of_duration = none;
next = none;
break;
}
}
DEBUG("times.interval", "stabilize: final start date = " << *start);
if (initial_start && (! start || *start < *initial_start)) {
resolve_end();
start = initial_start;
DEBUG("times.interval", "stabilize: start reset to initial start");
}
if (initial_end && (! end || *end > *initial_end)) {
end = initial_end;
DEBUG("times.interval", "stabilize: end reset to initial end");
}
}
aligned = true;
}
// If there is no duration, then if we've reached here the date falls // If there is no duration, then if we've reached here the date falls
// between begin and end. // between begin and end.
if (! duration) { if (! duration) {
DEBUG("times.interval", "stabilize: there was no duration given");
if (! start && ! end) if (! start && ! end)
throw_(date_error, throw_(date_error,
_("Invalid date interval: neither start, nor end, nor duration")); _("Invalid date interval: neither start, nor end, nor duration"));
return true; } else {
resolve_end();
}
}
bool date_interval_t::find_period(const date_t& date,
date_interval_t * last_interval)
{
stabilize(date);
if (end && date > *end) {
DEBUG("times.interval",
"false: date [" << date << "] > end [" << *end << "]");
return false;
} }
if (! end_of_duration) if (date < *start) {
end_of_duration = add_duration(*start, *duration); DEBUG("times.interval",
"false: date [" << date << "] < start [" << *start << "]");
return false;
}
if (! skip_duration) if (date < *end_of_duration) {
skip_duration = duration; DEBUG("times.interval",
"true: date [" << date << "] < end_of_duration ["
if (! next) << *end_of_duration << "]");
next = add_duration(*start, *skip_duration);
if (date < *end_of_duration)
return true; return true;
}
// If we've reached here, it means the date does not fall into the current // 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 // 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 // pass by date in so doing, which means we shouldn't alter the current
// period of the interval at all. // period of the interval at all.
date_t scan = *next; date_t scan = *start;
date_t end_of_scan = add_duration(scan, *duration); date_t end_of_scan = *end_of_duration;
DEBUG("times.interval", "date = " << date);
DEBUG("times.interval", "scan = " << scan);
DEBUG("times.interval", "end_of_scan = " << end_of_scan);
while (date >= scan && (! end || scan < *end)) { while (date >= scan && (! end || scan < *end)) {
if (date < end_of_scan) { if (date < end_of_scan) {
@ -262,12 +354,18 @@ bool date_interval_t::find_period(const date_t& date,
last_interval->next = next; last_interval->next = next;
last_interval->end_of_duration = end_of_duration; last_interval->end_of_duration = end_of_duration;
} }
start = scan; start = scan;
end_of_duration = end_of_scan; end_of_duration = end_of_scan;
next = none; next = none;
DEBUG("times.interval", "true: start = " << *start);
DEBUG("times.interval", "true: end_of_duration = " << *end_of_duration);
return true; return true;
} }
scan = add_duration(scan, *skip_duration);
scan = add_duration(scan, *skip_duration);
end_of_scan = add_duration(scan, *duration); end_of_scan = add_duration(scan, *duration);
} }
@ -279,20 +377,25 @@ date_interval_t& date_interval_t::operator++()
if (! start) if (! start)
throw_(date_error, _("Cannot increment an unstarted date interval")); throw_(date_error, _("Cannot increment an unstarted date interval"));
if (! skip_duration) { stabilize();
if (duration)
skip_duration = duration; if (! skip_duration || ! duration)
else throw_(date_error,
throw_(date_error, _("Cannot increment a date interval without a duration"));
_("Cannot increment a date interval without a duration"));
assert(next);
if (end && *next >= *end) {
start = none;
} else {
start = *next;
end_of_duration = add_duration(*start, *duration);
} }
*start = add_duration(*start, *skip_duration); next = none;
if (end && *start >= *end) resolve_end();
start = none;
else
end_of_duration = add_duration(*start, *duration);
return *this; return *this;
} }

View file

@ -138,6 +138,7 @@ public:
const duration_t& duration); const duration_t& duration);
optional<date_t> start; optional<date_t> start;
bool aligned;
optional<duration_t> skip_duration; optional<duration_t> skip_duration;
std::size_t factor; std::size_t factor;
optional<date_t> next; optional<date_t> next;
@ -145,10 +146,10 @@ public:
optional<date_t> end_of_duration; optional<date_t> end_of_duration;
optional<date_t> end; optional<date_t> end;
explicit date_interval_t() : factor(1) { explicit date_interval_t() : aligned(false), factor(1) {
TRACE_CTOR(date_interval_t, ""); TRACE_CTOR(date_interval_t, "");
} }
date_interval_t(const string& str) : factor(1) { date_interval_t(const string& str) : aligned(false), factor(1) {
TRACE_CTOR(date_interval_t, "const string&"); TRACE_CTOR(date_interval_t, "const string&");
parse(str); parse(str);
} }
@ -182,6 +183,9 @@ public:
parse(in); parse(in);
} }
void resolve_end();
void stabilize(const optional<date_t>& date = none);
bool is_valid() const { bool is_valid() const {
return start; return start;
} }