summaryrefslogtreecommitdiff
path: root/src/times.cc
diff options
context:
space:
mode:
authorJohn Wiegley <johnw@newartisans.com>2009-03-15 01:14:13 -0400
committerJohn Wiegley <johnw@newartisans.com>2009-03-15 22:39:44 -0400
commita05353e26928464b485767cc843ec5b3d9e47040 (patch)
treee46fa45354ecebceeca8b631893aef4edb0d281c /src/times.cc
parent1889d449b6f7321d711c00a33f17adfbb986287e (diff)
downloadfork-ledger-a05353e26928464b485767cc843ec5b3d9e47040.tar.gz
fork-ledger-a05353e26928464b485767cc843ec5b3d9e47040.tar.bz2
fork-ledger-a05353e26928464b485767cc843ec5b3d9e47040.zip
First iteration of the new date_interval_t rewrite
Diffstat (limited to 'src/times.cc')
-rw-r--r--src/times.cc336
1 files changed, 217 insertions, 119 deletions
diff --git a/src/times.cc b/src/times.cc
index d83b6baa..512d377b 100644
--- a/src/times.cc
+++ b/src/times.cc
@@ -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 (months > 0 || years > 0) {
- begin = date_t(moment->year(), gregorian::Jan, 1);
- } 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);
+}
+
+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);
+}
+
+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));
- // 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);
+ 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 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;
+ }
+
+ 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);
- if (moment && *moment >= quant) {
- date_t temp;
- while (*moment >= (temp = increment(quant))) {
- if (quant == temp)
- break;
- quant = temp;
+ 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 quant;
+
+ 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 (! skip_duration) {
+ if (duration)
+ skip_duration = duration;
+ else
+ throw_(date_error,
+ _("Cannot increment a date interval without a duration"));
+ }
+
+ *start = add_duration(*start, *skip_duration);
- if (years) future += gregorian::years(years);
- if (months) future += gregorian::months(months);
- if (days) future += gregorian::days(days);
+ if (end && *start >= *end)
+ start = none;
+ else
+ end_of_duration = add_duration(*start, *duration);
- return future;
+ 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;
+ date_t start = CURRENT_DATE();
+ date_t end;
+ bool parse_specifier = false;
+
+ 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 == _("year")) {
- int year = CURRENT_DATE().year();
- std::sprintf(buf, "%04d", year);
- word = buf;
+ else if (word == _("month")) {
+ duration = gregorian::months(1);
+ start = gregorian::date(start.year(), start.month(), 1);
}
- else if (word == _("today")) {
- if (begin)
- *begin = CURRENT_DATE();
- if (end) {
- *end = CURRENT_DATE();
- *end += gregorian::days(1);
- }
- return;
+ else if (word == _("today") || word == _("day")) {
+ duration = gregorian::days(1);
}
+ else {
+ parse_specifier = true;
+ }
+ end = date_interval_t::add_duration(start, duration);
- parse_inclusion_specifier(word, begin, end);
+ 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;
}
}
}