diff options
author | John Wiegley <johnw@newartisans.com> | 2009-03-15 04:03:17 -0400 |
---|---|---|
committer | John Wiegley <johnw@newartisans.com> | 2009-03-15 23:51:46 -0400 |
commit | 585b3a246d51ce50d3085d406079bc63588673dd (patch) | |
tree | 2837a6256b2d5ab5b52eef3feecc9546434469e4 /src/times.cc | |
parent | a05353e26928464b485767cc843ec5b3d9e47040 (diff) | |
download | fork-ledger-585b3a246d51ce50d3085d406079bc63588673dd.tar.gz fork-ledger-585b3a246d51ce50d3085d406079bc63588673dd.tar.bz2 fork-ledger-585b3a246d51ce50d3085d406079bc63588673dd.zip |
Added feature to "align" the interval's start date
Diffstat (limited to 'src/times.cc')
-rw-r--r-- | src/times.cc | 175 |
1 files changed, 139 insertions, 36 deletions
diff --git a/src/times.cc b/src/times.cc index 512d377b..2074f651 100644 --- a/src/times.cc +++ b/src/times.cc @@ -178,29 +178,56 @@ std::ostream& operator<<(std::ostream& out, { if (duration.type() == typeid(gregorian::days)) out << boost::get<gregorian::days>(duration).days() - << " days"; + << " day(s)"; else if (duration.type() == typeid(gregorian::weeks)) out << (boost::get<gregorian::weeks>(duration).days() / 7) - << " weeks"; + << " week(s)"; else if (duration.type() == typeid(gregorian::months)) out << boost::get<gregorian::months>(duration).number_of_months() - << " months"; + << " month(s)"; else { assert(duration.type() == typeid(gregorian::years)); out << boost::get<gregorian::years>(duration).number_of_years() - << " years"; + << " year(s)"; } return out; } -bool date_interval_t::find_period(const date_t& date, - date_interval_t * last_interval) +void date_interval_t::resolve_end() { - if (end && date > *end) - return false; + if (! end_of_duration) { + end_of_duration = add_duration(*start, *duration); + DEBUG("times.interval", + "stabilize: end_of_duration = " << *end_of_duration); + } + + 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); + } +} - if (! start) { +void date_interval_t::stabilize(const optional<date_t>& date) +{ + if (date && ! aligned) { + DEBUG("times.interval", "stabilize: date passed, but not aligned"); if (duration) { + DEBUG("times.interval", + "stabilize: aligning with a duration: " << *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. // @@ -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 // enough that we don't spend hundreds of thousands of loops skipping // 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) || - duration->type() == typeid(gregorian::weeks)) { - start = date_t(date.year(), gregorian::Jan, 1); + duration->type() == typeid(gregorian::years)) { + DEBUG("times.interval", "stabilize: monthly or yearly duration"); + + start = date_t(when.year(), gregorian::Jan, 1); } 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)) { // Move it to a Sunday @@ -220,40 +265,87 @@ bool date_interval_t::find_period(const date_t& date, *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; + } + } + + 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 (date < *start) - return false; - // If there is no duration, then if we've reached here the date falls // between begin and end. if (! duration) { + DEBUG("times.interval", "stabilize: there was no duration given"); + if (! start && ! end) throw_(date_error, _("Invalid date interval: neither start, nor end, nor duration")); - return true; + } else { + resolve_end(); } +} - if (! end_of_duration) - end_of_duration = add_duration(*start, *duration); +bool date_interval_t::find_period(const date_t& date, + date_interval_t * last_interval) +{ + stabilize(date); - if (! skip_duration) - skip_duration = duration; + if (end && date > *end) { + DEBUG("times.interval", + "false: date [" << date << "] > end [" << *end << "]"); + return false; + } - if (! next) - next = add_duration(*start, *skip_duration); + if (date < *start) { + 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; + } // 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); + date_t scan = *start; + 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)) { 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->end_of_duration = end_of_duration; } + start = scan; end_of_duration = end_of_scan; next = none; + + DEBUG("times.interval", "true: start = " << *start); + DEBUG("times.interval", "true: end_of_duration = " << *end_of_duration); + return true; } - scan = add_duration(scan, *skip_duration); + + scan = add_duration(scan, *skip_duration); end_of_scan = add_duration(scan, *duration); } @@ -279,20 +377,25 @@ date_interval_t& date_interval_t::operator++() 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")); - } + stabilize(); + + if (! skip_duration || ! duration) + throw_(date_error, + _("Cannot increment a date interval without a duration")); - *start = add_duration(*start, *skip_duration); + assert(next); - if (end && *start >= *end) + if (end && *next >= *end) { start = none; - else + } else { + start = *next; + end_of_duration = add_duration(*start, *duration); + } + + next = none; + + resolve_end(); return *this; } |