Skip to content
This repository

HTTPS clone URL

Subversion checkout URL

You can clone with HTTPS or Subversion.

Download ZIP
Browse code

First iteration of the new date_interval_t rewrite

  • Loading branch information...
commit a05353e26928464b485767cc843ec5b3d9e47040 1 parent 1889d44
John Wiegley jwiegley authored
76 src/filters.cc
@@ -562,7 +562,7 @@ void interval_posts::report_subtotal(const date_t& finish)
562 562 if (exact_periods)
563 563 subtotal_posts::report_subtotal();
564 564 else
565   - subtotal_posts::report_subtotal(NULL, interval.begin, finish);
  565 + subtotal_posts::report_subtotal(NULL, *interval.start, finish);
566 566 }
567 567
568 568 last_post = NULL;
@@ -572,49 +572,37 @@ void interval_posts::operator()(post_t& post)
572 572 {
573 573 date_t date = post.date();
574 574
575   - if ((is_valid(interval.begin) && date < interval.begin) ||
576   - (is_valid(interval.end) && date >= interval.end))
  575 + if (! interval.find_period(post.date(), &last_interval))
577 576 return;
578 577
579   - if (interval) {
580   - if (! is_valid(interval.begin))
581   - interval.set_start(date);
582   - start = interval.begin;
583   -
584   - date_t quant = interval.increment(interval.begin);
585   - if (date >= quant) {
586   - if (last_post)
587   - report_subtotal(quant - gregorian::days(1));
588   -
589   - date_t temp;
590   - while (date >= (temp = interval.increment(quant))) {
591   - if (quant == temp)
592   - break;
593   - interval.begin = quant;
594   - quant = temp;
  578 + if (interval.duration) {
  579 + if (last_interval) {
  580 + if (interval != last_interval) {
  581 + report_subtotal(last_interval.inclusive_end());
595 582
596 583 if (generate_empty_posts) {
597   - // Generate a null posting, so the intervening periods can be
598   - // seen when -E is used, or if the calculated amount ends up being
599   - // non-zero
600   - xact_temps.push_back(xact_t());
601   - xact_t& null_xact = xact_temps.back();
602   - null_xact.add_flags(ITEM_TEMP);
603   - null_xact._date = quant - gregorian::days(1);
604   -
605   - post_temps.push_back(post_t(&empty_account));
606   - post_t& null_post = post_temps.back();
607   - null_post.add_flags(ITEM_TEMP | POST_CALCULATED);
608   - null_post.amount = 0L;
609   - null_xact.add_post(&null_post);
610   -
611   - last_post = &null_post;
612   - subtotal_posts::operator()(null_post);
613   -
614   - report_subtotal(quant - gregorian::days(1));
  584 + for (++last_interval; interval != last_interval; ++last_interval) {
  585 + // Generate a null posting, so the intervening periods can be
  586 + // seen when -E is used, or if the calculated amount ends up being
  587 + // non-zero
  588 + xact_temps.push_back(xact_t());
  589 + xact_t& null_xact = xact_temps.back();
  590 + null_xact.add_flags(ITEM_TEMP);
  591 + null_xact._date = last_interval.inclusive_end();
  592 +
  593 + post_temps.push_back(post_t(&empty_account));
  594 + post_t& null_post = post_temps.back();
  595 + null_post.add_flags(ITEM_TEMP | POST_CALCULATED);
  596 + null_post.amount = 0L;
  597 + null_xact.add_post(&null_post);
  598 +
  599 + last_post = &null_post;
  600 + subtotal_posts::operator()(null_post);
  601 +
  602 + report_subtotal(last_interval.inclusive_end());
  603 + }
615 604 }
616 605 }
617   - start = interval.begin = quant;
618 606 }
619 607 subtotal_posts::operator()(post);
620 608 } else {
@@ -745,7 +733,7 @@ void generate_posts::add_period_xacts(period_xacts_list& period_xacts)
745 733 add_post(xact->period, *post);
746 734 }
747 735
748   -void generate_posts::add_post(const interval_t& period, post_t& post)
  736 +void generate_posts::add_post(const date_interval_t& period, post_t& post)
749 737 {
750 738 pending_posts.push_back(pending_posts_pair(period, &post));
751 739 }
@@ -759,6 +747,7 @@ void budget_posts::report_budget_items(const date_t& date)
759 747 do {
760 748 reported = false;
761 749 foreach (pending_posts_list::value_type& pair, pending_posts) {
  750 +#if 0
762 751 date_t& begin = pair.first.begin;
763 752 if (! is_valid(begin)) {
764 753 pair.first.set_start(date);
@@ -790,6 +779,7 @@ void budget_posts::report_budget_items(const date_t& date)
790 779
791 780 reported = true;
792 781 }
  782 +#endif
793 783 }
794 784 } while (reported);
795 785 }
@@ -823,11 +813,12 @@ void budget_posts::operator()(post_t& post)
823 813 }
824 814 }
825 815
826   -void forecast_posts::add_post(const interval_t& period, post_t& post)
  816 +void forecast_posts::add_post(const date_interval_t& period, post_t& post)
827 817 {
828 818 generate_posts::add_post(period, post);
829 819
830   - interval_t& i = pending_posts.back().first;
  820 + date_interval_t& i = pending_posts.back().first;
  821 +#if 0
831 822 if (! is_valid(i.begin)) {
832 823 i.set_start(CURRENT_DATE());
833 824 i.begin = i.increment(i.begin);
@@ -835,6 +826,7 @@ void forecast_posts::add_post(const interval_t& period, post_t& post)
835 826 while (i.begin < CURRENT_DATE())
836 827 i.begin = i.increment(i.begin);
837 828 }
  829 +#endif
838 830 }
839 831
840 832 void forecast_posts::flush()
@@ -842,6 +834,7 @@ void forecast_posts::flush()
842 834 posts_list passed;
843 835 date_t last;
844 836
  837 +#if 0
845 838 while (pending_posts.size() > 0) {
846 839 pending_posts_list::iterator least = pending_posts.begin();
847 840 for (pending_posts_list::iterator i = ++pending_posts.begin();
@@ -900,6 +893,7 @@ void forecast_posts::flush()
900 893 }
901 894 }
902 895 }
  896 +#endif
903 897
904 898 item_handler<post_t>::flush();
905 899 }
40 src/filters.h
@@ -558,22 +558,22 @@ class subtotal_posts : public item_handler<post_t>
558 558 */
559 559 class interval_posts : public subtotal_posts
560 560 {
561   - interval_t interval;
562   - post_t * last_post;
563   - account_t empty_account;
564   - bool exact_periods;
565   - bool generate_empty_posts;
566   - date_t start;
  561 + date_interval_t interval;
  562 + date_interval_t last_interval;
  563 + post_t * last_post;
  564 + account_t empty_account;
  565 + bool exact_periods;
  566 + bool generate_empty_posts;
567 567
568 568 interval_posts();
569 569
570 570 public:
571 571
572   - interval_posts(post_handler_ptr _handler,
573   - expr_t& amount_expr,
574   - const interval_t& _interval,
575   - bool _exact_periods = false,
576   - bool _generate_empty_posts = false)
  572 + interval_posts(post_handler_ptr _handler,
  573 + expr_t& amount_expr,
  574 + const date_interval_t& _interval,
  575 + bool _exact_periods = false,
  576 + bool _generate_empty_posts = false)
577 577 : subtotal_posts(_handler, amount_expr), interval(_interval),
578 578 last_post(NULL), empty_account(NULL, _("<None>")),
579 579 exact_periods(_exact_periods),
@@ -588,9 +588,11 @@ class interval_posts : public subtotal_posts
588 588 void report_subtotal(const date_t& finish);
589 589
590 590 virtual void flush() {
591   - if (last_post && interval)
592   - report_subtotal(interval.increment(interval.begin) - gregorian::days(1));
593   - subtotal_posts::flush();
  591 + if (last_post && interval.duration) {
  592 + if (interval.is_valid())
  593 + report_subtotal(interval.inclusive_end());
  594 + subtotal_posts::flush();
  595 + }
594 596 }
595 597 virtual void operator()(post_t& post);
596 598 };
@@ -725,11 +727,11 @@ class generate_posts : public item_handler<post_t>
725 727 generate_posts();
726 728
727 729 protected:
728   - typedef std::pair<interval_t, post_t *> pending_posts_pair;
729   - typedef std::list<pending_posts_pair> pending_posts_list;
  730 + typedef std::pair<date_interval_t, post_t *> pending_posts_pair;
  731 + typedef std::list<pending_posts_pair> pending_posts_list;
730 732
731 733 pending_posts_list pending_posts;
732   - std::list<xact_t> xact_temps;
  734 + std::list<xact_t> xact_temps;
733 735 std::list<post_t> post_temps;
734 736
735 737 public:
@@ -745,7 +747,7 @@ class generate_posts : public item_handler<post_t>
745 747
746 748 void add_period_xacts(period_xacts_list& period_xacts);
747 749
748   - virtual void add_post(const interval_t& period, post_t& post);
  750 + virtual void add_post(const date_interval_t& period, post_t& post);
749 751 };
750 752
751 753 /**
@@ -800,7 +802,7 @@ class forecast_posts : public generate_posts
800 802 TRACE_DTOR(forecast_posts);
801 803 }
802 804
803   - virtual void add_post(const interval_t& period, post_t& post);
  805 + virtual void add_post(const date_interval_t& period, post_t& post);
804 806 virtual void flush();
805 807 };
806 808
53 src/precmd.cc
@@ -160,25 +160,54 @@ value_t period_command(call_scope_t& args)
160 160 report_t& report(find_scope<report_t>(args));
161 161 std::ostream& out(report.output_stream);
162 162
163   - interval_t interval(arg);
  163 + date_interval_t interval(arg);
  164 +
  165 + out << _("global details => ") << std::endl << std::endl;
  166 +
  167 + if (interval.start)
  168 + out << _(" start: ") << format_date(*interval.start) << std::endl;
  169 + else
  170 + out << _(" start: TODAY: ") << format_date(CURRENT_DATE()) << std::endl;
  171 + if (interval.end)
  172 + out << _(" end: ") << format_date(*interval.end) << std::endl;
  173 +
  174 + if (interval.skip_duration)
  175 + out << _(" skip: ") << *interval.skip_duration << std::endl;
  176 + if (interval.factor)
  177 + out << _(" factor: ") << interval.factor << std::endl;
  178 + if (interval.duration)
  179 + out << _("duration: ") << *interval.duration << std::endl;
  180 +
  181 + if (interval.find_period(interval.start ?
  182 + *interval.start : CURRENT_DATE())) {
  183 + out << std::endl
  184 + << _("after finding first period => ") << std::endl
  185 + << std::endl;
  186 +
  187 + if (interval.start)
  188 + out << _(" start: ") << format_date(*interval.start) << std::endl;
  189 + if (interval.end)
  190 + out << _(" end: ") << format_date(*interval.end) << std::endl;
  191 +
  192 + if (interval.skip_duration)
  193 + out << _(" skip: ") << *interval.skip_duration << std::endl;
  194 + if (interval.factor)
  195 + out << _(" factor: ") << interval.factor << std::endl;
  196 + if (interval.duration)
  197 + out << _("duration: ") << *interval.duration << std::endl;
164 198
165   - if (! is_valid(interval.begin)) {
166   - out << _("Time period has no beginning.") << std::endl;
167   - } else {
168   - out << _("begin: ") << format_date(interval.begin) << std::endl;
169   - out << _(" end: ") << format_date(interval.end) << std::endl;
170 199 out << std::endl;
171 200
172   - date_t date = interval.first();
173   -
174   - for (int i = 0; i < 20; i++) {
  201 + for (int i = 0; i < 20 && interval; i++, ++interval) {
175 202 out << std::right;
176 203 out.width(2);
177 204
178   - out << i << "): " << format_date(date) << std::endl;
  205 + out << i << "): " << format_date(*interval.start);
  206 + if (interval.end_of_duration)
  207 + out << " -- " << format_date(interval.inclusive_end());
  208 + out << std::endl;
179 209
180   - date = interval.increment(date);
181   - if (is_valid(interval.end) && date >= interval.end)
  210 + if (! interval.skip_duration)
182 211 break;
183 212 }
184 213 }
12 src/report.h
@@ -240,14 +240,14 @@ class report_t : public scope_t
240 240 });
241 241
242 242 OPTION_(report_t, begin_, DO_(args) { // -b
243   - interval_t interval(args[0].to_string());
244   - if (! is_valid(interval.begin))
  243 + date_interval_t interval(args[0].to_string());
  244 + if (! interval.start)
245 245 throw_(std::invalid_argument,
246 246 _("Could not determine beginning of period '%1'")
247 247 << args[0].to_string());
248 248
249 249 string predicate =
250   - "date>=[" + to_iso_extended_string(interval.begin) + "]";
  250 + "date>=[" + to_iso_extended_string(*interval.start) + "]";
251 251 parent->HANDLER(limit_).on(predicate);
252 252 });
253 253
@@ -355,14 +355,14 @@ class report_t : public scope_t
355 355 OPTION(report_t, empty); // -E
356 356
357 357 OPTION_(report_t, end_, DO_(args) { // -e
358   - interval_t interval(args[0].to_string());
359   - if (! is_valid(interval.begin))
  358 + date_interval_t interval(args[0].to_string());
  359 + if (! interval.start)
360 360 throw_(std::invalid_argument,
361 361 _("Could not determine end of period '%1'")
362 362 << args[0].to_string());
363 363
364 364 string predicate =
365   - "date<[" + to_iso_extended_string(interval.begin) + "]";
  365 + "date<[" + to_iso_extended_string(*interval.start) + "]";
366 366 parent->HANDLER(limit_).on(predicate);
367 367 #if 0
368 368 terminus = interval.begin;
336 src/times.cc
@@ -107,25 +107,25 @@ namespace {
107 107 }
108 108 }
109 109
110   -int string_to_day_of_week(const std::string& str)
  110 +date_time::weekdays string_to_day_of_week(const std::string& str)
111 111 {
112 112 if (str == _("sun") || str == _("sunday") || str == "0")
113   - return 0;
  113 + return gregorian::Sunday;
114 114 else if (str == _("mon") || str == _("monday") || str == "1")
115   - return 1;
  115 + return gregorian::Monday;
116 116 else if (str == _("tue") || str == _("tuesday") || str == "2")
117   - return 2;
  117 + return gregorian::Tuesday;
118 118 else if (str == _("wed") || str == _("wednesday") || str == "3")
119   - return 3;
  119 + return gregorian::Wednesday;
120 120 else if (str == _("thu") || str == _("thursday") || str == "4")
121   - return 4;
  121 + return gregorian::Thursday;
122 122 else if (str == _("fri") || str == _("friday") || str == "5")
123   - return 5;
  123 + return gregorian::Friday;
124 124 else if (str == _("sat") || str == _("saturday") || str == "6")
125   - return 6;
  125 + return gregorian::Saturday;
126 126
127 127 assert(false);
128   - return -1;
  128 + return gregorian::Sunday;
129 129 }
130 130
131 131 datetime_t parse_datetime(const char * str, int)
@@ -145,50 +145,156 @@ date_t parse_date(const char * str, int current_year)
145 145 return gregorian::date_from_tm(when);
146 146 }
147 147
148   -date_t interval_t::first(const optional<date_t>& moment)
  148 +date_t date_interval_t::add_duration(const date_t& date,
  149 + const duration_t& duration)
149 150 {
150   - if (! is_valid(begin)) {
151   - // Find an efficient starting point for the upcoming while loop. We want
152   - // a date early enough that the range will be correct, but late enough
153   - // that we don't spend hundreds of thousands of loops skipping through
154   - // time.
155   - assert(moment);
156   -
157   - if (months > 0 || years > 0) {
158   - begin = date_t(moment->year(), gregorian::Jan, 1);
159   - } else {
160   - begin = date_t(*moment - gregorian::days(400));
  151 + if (duration.type() == typeid(gregorian::days))
  152 + return date + boost::get<gregorian::days>(duration);
  153 + else if (duration.type() == typeid(gregorian::weeks))
  154 + return date + boost::get<gregorian::weeks>(duration);
  155 + else if (duration.type() == typeid(gregorian::months))
  156 + return date + boost::get<gregorian::months>(duration);
  157 + else
  158 + assert(duration.type() == typeid(gregorian::years));
  159 + return date + boost::get<gregorian::years>(duration);
  160 +}
  161 +
  162 +date_t date_interval_t::subtract_duration(const date_t& date,
  163 + const duration_t& duration)
  164 +{
  165 + if (duration.type() == typeid(gregorian::days))
  166 + return date - boost::get<gregorian::days>(duration);
  167 + else if (duration.type() == typeid(gregorian::weeks))
  168 + return date - boost::get<gregorian::weeks>(duration);
  169 + else if (duration.type() == typeid(gregorian::months))
  170 + return date - boost::get<gregorian::months>(duration);
  171 + else
  172 + assert(duration.type() == typeid(gregorian::years));
  173 + return date - boost::get<gregorian::years>(duration);
  174 +}
  175 +
  176 +std::ostream& operator<<(std::ostream& out,
  177 + const date_interval_t::duration_t& duration)
  178 +{
  179 + if (duration.type() == typeid(gregorian::days))
  180 + out << boost::get<gregorian::days>(duration).days()
  181 + << " days";
  182 + else if (duration.type() == typeid(gregorian::weeks))
  183 + out << (boost::get<gregorian::weeks>(duration).days() / 7)
  184 + << " weeks";
  185 + else if (duration.type() == typeid(gregorian::months))
  186 + out << boost::get<gregorian::months>(duration).number_of_months()
  187 + << " months";
  188 + else {
  189 + assert(duration.type() == typeid(gregorian::years));
  190 + out << boost::get<gregorian::years>(duration).number_of_years()
  191 + << " years";
  192 + }
  193 + return out;
  194 +}
  195 +
  196 +bool date_interval_t::find_period(const date_t& date,
  197 + date_interval_t * last_interval)
  198 +{
  199 + if (end && date > *end)
  200 + return false;
  201 +
  202 + if (! start) {
  203 + if (duration) {
  204 + // The interval object has not been seeded with a start date yet, so
  205 + // find the nearest period before on on date which fits, if possible.
  206 + //
  207 + // Find an efficient starting point for the upcoming while loop. We
  208 + // want a date early enough that the range will be correct, but late
  209 + // enough that we don't spend hundreds of thousands of loops skipping
  210 + // through time.
  211 + if (duration->type() == typeid(gregorian::months) ||
  212 + duration->type() == typeid(gregorian::weeks)) {
  213 + start = date_t(date.year(), gregorian::Jan, 1);
  214 + } else {
  215 + start = date_t(date - gregorian::days(400));
161 216
162   - // jww (2009-02-21): Add support for starting a week on any day
163   - if (weekly) { // move it to a Sunday
164   - while (begin.day_of_week() != start_of_week)
165   - begin += gregorian::days(1);
  217 + if (duration->type() == typeid(gregorian::weeks)) {
  218 + // Move it to a Sunday
  219 + while (start->day_of_week() != start_of_week)
  220 + *start += gregorian::days(1);
  221 + }
166 222 }
167 223 }
168 224 }
169 225
170   - date_t quant(begin);
  226 + if (date < *start)
  227 + return false;
  228 +
  229 + // If there is no duration, then if we've reached here the date falls
  230 + // between begin and end.
  231 + if (! duration) {
  232 + if (! start && ! end)
  233 + throw_(date_error,
  234 + _("Invalid date interval: neither start, nor end, nor duration"));
  235 + return true;
  236 + }
  237 +
  238 + if (! end_of_duration)
  239 + end_of_duration = add_duration(*start, *duration);
  240 +
  241 + if (! skip_duration)
  242 + skip_duration = duration;
  243 +
  244 + if (! next)
  245 + next = add_duration(*start, *skip_duration);
  246 +
  247 + if (date < *end_of_duration)
  248 + return true;
  249 +
  250 + // If we've reached here, it means the date does not fall into the current
  251 + // interval, so we must seek another interval that does match -- unless we
  252 + // pass by date in so doing, which means we shouldn't alter the current
  253 + // period of the interval at all.
  254 +
  255 + date_t scan = *next;
  256 + date_t end_of_scan = add_duration(scan, *duration);
171 257
172   - if (moment && *moment >= quant) {
173   - date_t temp;
174   - while (*moment >= (temp = increment(quant))) {
175   - if (quant == temp)
176   - break;
177   - quant = temp;
  258 + while (date >= scan && (! end || scan < *end)) {
  259 + if (date < end_of_scan) {
  260 + if (last_interval) {
  261 + last_interval->start = start;
  262 + last_interval->next = next;
  263 + last_interval->end_of_duration = end_of_duration;
  264 + }
  265 + start = scan;
  266 + end_of_duration = end_of_scan;
  267 + next = none;
  268 + return true;
178 269 }
  270 + scan = add_duration(scan, *skip_duration);
  271 + end_of_scan = add_duration(scan, *duration);
179 272 }
180   - return quant;
  273 +
  274 + return false;
181 275 }
182 276
183   -date_t interval_t::increment(const date_t& moment) const
  277 +date_interval_t& date_interval_t::operator++()
184 278 {
185   - date_t future(moment);
  279 + if (! start)
  280 + throw_(date_error, _("Cannot increment an unstarted date interval"));
  281 +
  282 + if (! skip_duration) {
  283 + if (duration)
  284 + skip_duration = duration;
  285 + else
  286 + throw_(date_error,
  287 + _("Cannot increment a date interval without a duration"));
  288 + }
  289 +
  290 + *start = add_duration(*start, *skip_duration);
186 291
187   - if (years) future += gregorian::years(years);
188   - if (months) future += gregorian::months(months);
189   - if (days) future += gregorian::days(days);
  292 + if (end && *start >= *end)
  293 + start = none;
  294 + else
  295 + end_of_duration = add_duration(*start, *duration);
190 296
191   - return future;
  297 + return *this;
192 298 }
193 299
194 300 namespace {
@@ -230,9 +336,15 @@ namespace {
230 336
231 337 if (begin) {
232 338 *begin = gregorian::date_from_tm(when);
233   - if (end)
234   - *end = interval_t(saw_day ? 1 : 0, saw_mon ? 1 : 0,
235   - saw_year ? 1 : 0).increment(*begin);
  339 +
  340 + if (end) {
  341 + if (saw_year)
  342 + *end = *begin + gregorian::years(1);
  343 + else if (saw_mon)
  344 + *end = *begin + gregorian::months(1);
  345 + else if (saw_day)
  346 + *end = *begin + gregorian::days(1);
  347 + }
236 348 }
237 349 else if (end) {
238 350 *end = gregorian::date_from_tm(when);
@@ -245,14 +357,14 @@ namespace {
245 357 word[i] = static_cast<char>(std::tolower(word[i]));
246 358 }
247 359
248   - void parse_date_words(std::istream& in, string& word,
249   - date_t * begin, date_t * end)
  360 + void parse_date_words(std::istream& in,
  361 + string& word,
  362 + date_interval_t& interval,
  363 + bool look_for_start = true,
  364 + bool look_for_end = true)
250 365 {
251 366 string type;
252 367
253   - bool mon_spec = false;
254   - char buf[32];
255   -
256 368 if (word == _("this") || word == _("last") || word == _("next")) {
257 369 type = word;
258 370 if (! in.eof())
@@ -263,59 +375,48 @@ namespace {
263 375 type = _("this");
264 376 }
265 377
266   - if (word == _("month")) {
267   - time_t now = to_time_t(CURRENT_TIME());
268   - std::strftime(buf, 31, "%B", localtime(&now));
269   - word = buf;
270   - mon_spec = true;
  378 + date_t start = CURRENT_DATE();
  379 + date_t end;
  380 + bool parse_specifier = false;
  381 +
  382 + date_interval_t::duration_t duration;
  383 +
  384 + assert(look_for_start || look_for_end);
  385 +
  386 + if (word == _("year")) {
  387 + duration = gregorian::years(1);
  388 + start = gregorian::date(start.year(), 1, 1);
271 389 }
272   - else if (word == _("year")) {
273   - int year = CURRENT_DATE().year();
274   - std::sprintf(buf, "%04d", year);
275   - word = buf;
  390 + else if (word == _("month")) {
  391 + duration = gregorian::months(1);
  392 + start = gregorian::date(start.year(), start.month(), 1);
276 393 }
277   - else if (word == _("today")) {
278   - if (begin)
279   - *begin = CURRENT_DATE();
280   - if (end) {
281   - *end = CURRENT_DATE();
282   - *end += gregorian::days(1);
283   - }
284   - return;
  394 + else if (word == _("today") || word == _("day")) {
  395 + duration = gregorian::days(1);
285 396 }
  397 + else {
  398 + parse_specifier = true;
  399 + }
  400 + end = date_interval_t::add_duration(start, duration);
286 401
287   - parse_inclusion_specifier(word, begin, end);
  402 + if (parse_specifier)
  403 + parse_inclusion_specifier(word, &start, &end);
288 404
289 405 if (type == _("last")) {
290   - if (mon_spec) {
291   - if (begin)
292   - *begin = interval_t(0, -1, 0).increment(*begin);
293   - if (end)
294   - *end = interval_t(0, -1, 0).increment(*end);
295   - } else {
296   - if (begin)
297   - *begin = interval_t(0, 0, -1).increment(*begin);
298   - if (end)
299   - *end = interval_t(0, 0, -1).increment(*end);
300   - }
  406 + start = date_interval_t::subtract_duration(start, duration);
  407 + end = date_interval_t::subtract_duration(end, duration);
301 408 }
302 409 else if (type == _("next")) {
303   - if (mon_spec) {
304   - if (begin)
305   - *begin = interval_t(0, 1, 0).increment(*begin);
306   - if (end)
307   - *end = interval_t(0, 1, 0).increment(*end);
308   - } else {
309   - if (begin)
310   - *begin = interval_t(0, 0, 1).increment(*begin);
311   - if (end)
312   - *end = interval_t(0, 0, 1).increment(*end);
313   - }
  410 + start = date_interval_t::add_duration(start, duration);
  411 + end = date_interval_t::add_duration(end, duration);
314 412 }
  413 +
  414 + if (look_for_start) interval.start = start;
  415 + if (look_for_end) interval.end = end;
315 416 }
316 417 }
317 418
318   -void interval_t::parse(std::istream& in)
  419 +void date_interval_t::parse(std::istream& in)
319 420 {
320 421 string word;
321 422
@@ -327,65 +428,62 @@ void interval_t::parse(std::istream& in)
327 428 int quantity = lexical_cast<int>(word);
328 429 read_lower_word(in, word);
329 430 if (word == _("days"))
330   - days = quantity;
331   - else if (word == _("weeks")) {
332   - days = 7 * quantity;
333   - weekly = true;
334   - }
  431 + duration = gregorian::days(quantity);
  432 + else if (word == _("weeks"))
  433 + duration = gregorian::weeks(quantity);
335 434 else if (word == _("months"))
336   - months = quantity;
  435 + duration = gregorian::months(quantity);
337 436 else if (word == _("quarters"))
338   - months = 3 * quantity;
  437 + duration = gregorian::months(3 * quantity);
339 438 else if (word == _("years"))
340   - years = quantity;
  439 + duration = gregorian::years(quantity);
341 440 }
342 441 else if (word == _("day"))
343   - days = 1;
344   - else if (word == _("week")) {
345   - days = 7;
346   - weekly = true;
347   - }
  442 + duration = gregorian::days(1);
  443 + else if (word == _("week"))
  444 + duration = gregorian::weeks(1);
348 445 else if (word == _("month"))
349   - months = 1;
  446 + duration = gregorian::months(1);
350 447 else if (word == _("quarter"))
351   - months = 3;
  448 + duration = gregorian::months(3);
352 449 else if (word == _("year"))
353   - years = 1;
  450 + duration = gregorian::years(1);
354 451 }
355 452 else if (word == _("daily"))
356   - days = 1;
357   - else if (word == _("weekly")) {
358   - days = 7;
359   - weekly = true;
360   - }
  453 + duration = gregorian::days(1);
  454 + else if (word == _("weekly"))
  455 + duration = gregorian::weeks(1);
361 456 else if (word == _("biweekly"))
362   - days = 14;
  457 + duration = gregorian::weeks(2);
363 458 else if (word == _("monthly"))
364   - months = 1;
  459 + duration = gregorian::months(1);
365 460 else if (word == _("bimonthly"))
366   - months = 2;
  461 + duration = gregorian::months(2);
367 462 else if (word == _("quarterly"))
368   - months = 3;
  463 + duration = gregorian::months(3);
369 464 else if (word == _("yearly"))
370   - years = 1;
  465 + duration = gregorian::years(1);
371 466 else if (word == _("this") || word == _("last") || word == _("next") ||
372 467 word == _("today")) {
373   - parse_date_words(in, word, &begin, &end);
  468 + parse_date_words(in, word, *this);
374 469 }
375 470 else if (word == _("in")) {
376 471 read_lower_word(in, word);
377   - parse_date_words(in, word, &begin, &end);
  472 + parse_date_words(in, word, *this);
378 473 }
379 474 else if (word == _("from") || word == _("since")) {
380 475 read_lower_word(in, word);
381   - parse_date_words(in, word, &begin, NULL);
  476 + parse_date_words(in, word, *this, true, false);
382 477 }
383 478 else if (word == _("to") || word == _("until")) {
384 479 read_lower_word(in, word);
385   - parse_date_words(in, word, NULL, &end);
  480 + parse_date_words(in, word, *this, false, true);
386 481 }
387 482 else {
388   - parse_inclusion_specifier(word, &begin, &end);
  483 + date_t b, e;
  484 + parse_inclusion_specifier(word, &b, &e);
  485 + start = b;
  486 + end = e;
389 487 }
390 488 }
391 489 }
132 src/times.h
@@ -60,6 +60,7 @@ inline bool is_valid(const datetime_t& moment) {
60 60
61 61 typedef boost::gregorian::date date_t;
62 62 typedef boost::gregorian::date_duration date_duration_t;
  63 +typedef boost::gregorian::date_iterator date_iterator_t;
63 64
64 65 inline bool is_valid(const date_t& moment) {
65 66 return ! moment.is_not_a_date();
@@ -75,7 +76,7 @@ inline bool is_valid(const date_t& moment) {
75 76 extern int start_of_week;
76 77 extern optional<std::string> input_date_format;
77 78
78   -int string_to_day_of_week(const std::string& str);
  79 +date_time::weekdays string_to_day_of_week(const std::string& str);
79 80
80 81 datetime_t parse_datetime(const char * str, int current_year = -1);
81 82
@@ -123,94 +124,83 @@ inline std::string format_date(const date_t& when,
123 124 return buf;
124 125 }
125 126
126   -/**
127   - * @brief Brief
128   - *
129   - * Long.
130   - */
131   -struct range_t
  127 +class date_interval_t : public equality_comparable<date_interval_t>
132 128 {
133   - date_t begin;
134   - date_t end;
135   -
136   - date_range_t(const date_t& _begin = date_t(),
137   - const date_t& _end = date_t())
138   - : begin(_begin), end(_end) {
139   - TRACE_CTOR(date_range_t, "const date_t&, const date_t&");
  129 +public:
  130 + typedef variant<gregorian::days,
  131 + gregorian::weeks,
  132 + gregorian::months,
  133 + gregorian::years> duration_t;
  134 +
  135 + static date_t add_duration(const date_t& date,
  136 + const duration_t& duration);
  137 + static date_t subtract_duration(const date_t& date,
  138 + const duration_t& duration);
  139 +
  140 + optional<date_t> start;
  141 + optional<duration_t> skip_duration;
  142 + std::size_t factor;
  143 + optional<date_t> next;
  144 + optional<duration_t> duration;
  145 + optional<date_t> end_of_duration;
  146 + optional<date_t> end;
  147 +
  148 + explicit date_interval_t() : factor(1) {
  149 + TRACE_CTOR(date_interval_t, "");
  150 + }
  151 + date_interval_t(const string& str) : factor(1) {
  152 + TRACE_CTOR(date_interval_t, "const string&");
  153 + parse(str);
140 154 }
141   - date_range_t(const date_range_t& other)
142   - : begin(other.begin), end(other.end) {
143   - TRACE_CTOR(date_range_t, "copy");
  155 + date_interval_t(const date_interval_t& other)
  156 + : start(other.start),
  157 + skip_duration(other.skip_duration),
  158 + factor(other.factor),
  159 + next(other.next),
  160 + duration(other.duration),
  161 + end_of_duration(other.end_of_duration),
  162 + end(other.end) {
  163 + TRACE_CTOR(date_interval_t, "copy");
144 164 }
145   - date_range_t(const string& desc) : begin(), end() {
146   - TRACE_CTOR(date_range_t, "const string&");
147   - std::istringstream stream(desc);
148   - parse(stream);
  165 + ~date_interval_t() throw() {
  166 + TRACE_DTOR(date_interval_t);
149 167 }
150   - ~date_range_t() throw() {
151   - TRACE_DTOR(date_range_t);
  168 +
  169 + bool operator==(const date_interval_t& other) const {
  170 + return (start == other.start &&
  171 + (! start || *start == *other.start));
152 172 }
153 173
154   - bool date_in_range(const date_t& date) {
155   - return ((! is_valid(begin) || date >= begin) &&
156   - (! is_valid(end) || date < end));
  174 + operator bool() const {
  175 + return is_valid();
157 176 }
158 177
159 178 void parse(std::istream& in);
160   -};
161 179
162   -/**
163   - * @brief Brief
164   - *
165   - * Long.
166   - */
167   -struct interval_t
168   -{
169   - int years;
170   - int months;
171   - int days;
172   - bool weekly;
173   -
174   - interval_t(int _days = 0,
175   - int _months = 0,
176   - int _years = 0,
177   - bool _weekly = false)
178   - : years(_years), months(_months), days(_days), weekly(_weekly) {
179   - TRACE_CTOR(interval_t, "int, int, int, bool");
180   - }
181   - interval_t(const interval_t& other)
182   - : years(other.years),
183   - months(other.months),
184   - days(other.days),
185   - weekly(other.weekly) {
186   - TRACE_CTOR(interval_t, "copy");
187   - }
188   - interval_t(const string& desc)
189   - : years(0), months(0), days(0), weekly(false) {
190   - TRACE_CTOR(interval_t, "const string&");
191   - std::istringstream stream(desc);
192   - parse(stream);
193   - }
194   - ~interval_t() throw() {
195   - TRACE_DTOR(interval_t);
  180 + void parse(const string& str) {
  181 + std::istringstream in(str);
  182 + parse(in);
196 183 }
197 184
198   - operator bool() const {
199   - return years != 0 || months != 0 || days != 0;
  185 + bool is_valid() const {
  186 + return start;
200 187 }
201 188
202   -#if 0
203   - void set_start(const date_t& moment) {
204   - begin = first(moment);
205   - }
206   -#endif
  189 + /** Find the current or next period containing date. Returns true if the
  190 + date_interval_t object has been altered to reflect the interval
  191 + containing date, or false if no such period can be found. */
  192 + bool find_period(const date_t& date, date_interval_t * last_interval = NULL);
207 193
208   - date_t first(const optional<date_t>& moment = none);
209   - date_t increment(const date_t&) const;
  194 + date_t inclusive_end() const {
  195 + return *end_of_duration - gregorian::days(1);
  196 + }
210 197
211   - void parse(std::istream& in);
  198 + date_interval_t& operator++();
212 199 };
213 200
  201 +std::ostream& operator<<(std::ostream& out,
  202 + const date_interval_t::duration_t& duration);
  203 +
214 204 } // namespace ledger
215 205
216 206 #endif // _TIMES_H
7 src/token.cc
@@ -205,9 +205,12 @@ void expr_t::token_t::next(std::istream& in, const uint_least8_t pflags)
205 205 in.get(c);
206 206 length++;
207 207
208   - interval_t timespan(buf);
  208 + date_interval_t timespan(buf);
  209 + if (! timespan)
  210 + throw_(parse_error,
  211 + _("Date specifier does not refer to a starting date"));
209 212 kind = VALUE;
210   - value = timespan.first();
  213 + value = *timespan.start;
211 214 break;
212 215 }
213 216
6 src/xact.h
@@ -186,8 +186,8 @@ struct auto_xact_finalizer_t : public xact_finalizer_t
186 186 class period_xact_t : public xact_base_t
187 187 {
188 188 public:
189   - interval_t period;
190   - string period_string;
  189 + date_interval_t period;
  190 + string period_string;
191 191
192 192 period_xact_t() {
193 193 TRACE_CTOR(period_xact_t, "");
@@ -206,7 +206,7 @@ class period_xact_t : public xact_base_t
206 206 }
207 207
208 208 virtual bool valid() const {
209   - return period;
  209 + return period.is_valid();
210 210 }
211 211 };
212 212

0 comments on commit a05353e

Please sign in to comment.
Something went wrong with that request. Please try again.