Added feature to "align" the interval's start date
This commit is contained in:
parent
a05353e269
commit
585b3a246d
2 changed files with 149 additions and 42 deletions
169
src/times.cc
169
src/times.cc
|
|
@ -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);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
DEBUG("times.interval",
|
||||||
|
"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;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (date < *start)
|
DEBUG("times.interval", "stabilize: final start date = " << *start);
|
||||||
return false;
|
|
||||||
|
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();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (! end_of_duration)
|
bool date_interval_t::find_period(const date_t& date,
|
||||||
end_of_duration = add_duration(*start, *duration);
|
date_interval_t * last_interval)
|
||||||
|
{
|
||||||
|
stabilize(date);
|
||||||
|
|
||||||
if (! skip_duration)
|
if (end && date > *end) {
|
||||||
skip_duration = duration;
|
DEBUG("times.interval",
|
||||||
|
"false: date [" << date << "] > end [" << *end << "]");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
if (! next)
|
if (date < *start) {
|
||||||
next = add_duration(*start, *skip_duration);
|
DEBUG("times.interval",
|
||||||
|
"false: date [" << date << "] < start [" << *start << "]");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
if (date < *end_of_duration)
|
if (date < *end_of_duration) {
|
||||||
|
DEBUG("times.interval",
|
||||||
|
"true: date [" << date << "] < end_of_duration ["
|
||||||
|
<< *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,11 +354,17 @@ 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;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -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;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Add table
Reference in a new issue