1 /*
2  * Copyright (c) 2003-2019, John Wiegley.  All rights reserved.
3  *
4  * Redistribution and use in source and binary forms, with or without
5  * modification, are permitted provided that the following conditions are
6  * met:
7  *
8  * - Redistributions of source code must retain the above copyright
9  *   notice, this list of conditions and the following disclaimer.
10  *
11  * - Redistributions in binary form must reproduce the above copyright
12  *   notice, this list of conditions and the following disclaimer in the
13  *   documentation and/or other materials provided with the distribution.
14  *
15  * - Neither the name of New Artisans LLC nor the names of its
16  *   contributors may be used to endorse or promote products derived from
17  *   this software without specific prior written permission.
18  *
19  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
20  * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
21  * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
22  * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
23  * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
24  * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
25  * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
26  * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
27  * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
28  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
29  * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
30  */
31 
32 #include <system.hh>
33 
34 #include "times.h"
35 
36 #if defined(_WIN32) || defined(__CYGWIN__)
37 #include "strptime.h"
38 #endif
39 
40 namespace ledger {
41 
42 optional<datetime_t> epoch;
43 
44 date_time::weekdays start_of_week = gregorian::Sunday;
45 
46 namespace {
47   template <typename T, typename InputFacetType, typename OutputFacetType>
48   class temporal_io_t : public noncopyable
49   {
50     string fmt_str;
51 
52   public:
53     date_traits_t traits;
54     bool input;
55 
temporal_io_t(const char * _fmt_str,bool _input)56     temporal_io_t(const char * _fmt_str, bool _input)
57       : fmt_str(_fmt_str),
58         traits(icontains(fmt_str, "%F") || icontains(fmt_str, "%y"),
59                icontains(fmt_str, "%F") || icontains(fmt_str, "%m") || icontains(fmt_str, "%b"),
60                icontains(fmt_str, "%F") || icontains(fmt_str, "%d")),
61         input(_input) {
62     }
63 
set_format(const char * fmt)64     void set_format(const char * fmt) {
65       fmt_str  = fmt;
66       traits   = date_traits_t(icontains(fmt_str, "%F") || icontains(fmt_str, "%y"),
67                                icontains(fmt_str, "%F") ||
68                                icontains(fmt_str, "%m") || icontains(fmt_str, "%b"),
69                                icontains(fmt_str, "%F") || icontains(fmt_str, "%d"));
70     }
71 
parse(const char *)72     T parse(const char *) {}
73 
format(const T & when)74     std::string format(const T& when) {
75       std::tm data(to_tm(when));
76       char buf[128];
77       std::strftime(buf, 127, fmt_str.c_str(), &data);
78       return buf;
79     }
80   };
81 
82   template <>
83   datetime_t temporal_io_t<datetime_t, posix_time::time_input_facet,
84                            posix_time::time_facet>
parse(const char * str)85     ::parse(const char * str)
86   {
87     std::tm data;
88     std::memset(&data, 0, sizeof(std::tm));
89     if (strptime(str, fmt_str.c_str(), &data))
90       return posix_time::ptime_from_tm(data);
91     else
92       return datetime_t();
93   }
94 
95   template <>
96   date_t temporal_io_t<date_t, gregorian::date_input_facet,
97                        gregorian::date_facet>
parse(const char * str)98     ::parse(const char * str)
99   {
100     std::tm data;
101     std::memset(&data, 0, sizeof(std::tm));
102     data.tm_year = CURRENT_DATE().year() - 1900;
103     data.tm_mday = 1;           // some formats have no day
104     if (strptime(str, fmt_str.c_str(), &data))
105       return gregorian::date_from_tm(data);
106     else
107       return date_t();
108   }
109 
110   typedef temporal_io_t<datetime_t, posix_time::time_input_facet,
111                         posix_time::time_facet> datetime_io_t;
112   typedef temporal_io_t<date_t, gregorian::date_input_facet,
113                         gregorian::date_facet> date_io_t;
114 
115   shared_ptr<datetime_io_t> input_datetime_io;
116   shared_ptr<datetime_io_t> timelog_datetime_io;
117   shared_ptr<datetime_io_t> written_datetime_io;
118   shared_ptr<date_io_t>     written_date_io;
119   shared_ptr<datetime_io_t> printed_datetime_io;
120   shared_ptr<date_io_t>     printed_date_io;
121 
122   std::deque<shared_ptr<date_io_t> > readers;
123 
124   bool convert_separators_to_slashes = true;
125 
parse_date_mask_routine(const char * date_str,date_io_t & io,date_traits_t * traits=NULL)126   date_t parse_date_mask_routine(const char * date_str, date_io_t& io,
127                                  date_traits_t * traits = NULL)
128   {
129     if (std::strlen(date_str) > 127) {
130         throw_(date_error, _f("Invalid date: %1%") % date_str);
131     }
132 
133     char buf[128];
134     std::strcpy(buf, date_str);
135 
136     if (convert_separators_to_slashes) {
137       for (char * p = buf; *p; p++)
138         if (*p == '.' || *p == '-')
139           *p = '/';
140     }
141 
142     date_t when = io.parse(buf);
143 
144     if (! when.is_not_a_date()) {
145       DEBUG("times.parse", "Passed date string:  " << date_str);
146       DEBUG("times.parse", "Parsed date string:  " << buf);
147       DEBUG("times.parse", "Parsed result is:    " << when);
148       DEBUG("times.parse", "Formatted result is: " << io.format(when));
149 
150       string when_str = io.format(when);
151 
152       const char * p = when_str.c_str();
153       const char * q = buf;
154       for (; *p && *q; p++, q++) {
155         if (*p != *q && *p == '0') p++;
156         if (! *p || *p != *q) break;
157       }
158       if (*p != '\0' || *q != '\0')
159         throw_(date_error, _f("Invalid date: %1%") % date_str);
160 
161       if (traits)
162         *traits = io.traits;
163 
164       if (! io.traits.has_year) {
165         when = date_t(CURRENT_DATE().year(), when.month(), when.day());
166 
167         if (when.month() > CURRENT_DATE().month())
168           when -= gregorian::years(1);
169       }
170     }
171     return when;
172   }
173 
parse_date_mask(const char * date_str,date_traits_t * traits=NULL)174   date_t parse_date_mask(const char * date_str, date_traits_t * traits = NULL)
175   {
176     foreach (shared_ptr<date_io_t>& reader, readers) {
177       date_t when = parse_date_mask_routine(date_str, *reader.get(), traits);
178       if (! when.is_not_a_date())
179         return when;
180     }
181 
182     throw_(date_error, _f("Invalid date: %1%") % date_str);
183     return date_t();
184   }
185 }
186 
string_to_day_of_week(const std::string & str)187 optional<date_time::weekdays> string_to_day_of_week(const std::string& str)
188 {
189   if (str == _("sun") || str == _("sunday") || str == "0")
190     return gregorian::Sunday;
191   else if (str == _("mon") || str == _("monday") || str == "1")
192     return gregorian::Monday;
193   else if (str == _("tue") || str == _("tuesday") || str == "2")
194     return gregorian::Tuesday;
195   else if (str == _("wed") || str == _("wednesday") || str == "3")
196     return gregorian::Wednesday;
197   else if (str == _("thu") || str == _("thursday") || str == "4")
198     return gregorian::Thursday;
199   else if (str == _("fri") || str == _("friday") || str == "5")
200     return gregorian::Friday;
201   else if (str == _("sat") || str == _("saturday") || str == "6")
202     return gregorian::Saturday;
203   else
204     return none;
205 }
206 
207 optional<date_time::months_of_year>
string_to_month_of_year(const std::string & str)208 string_to_month_of_year(const std::string& str)
209 {
210   if (str == _("jan") || str == _("january") || str == "0")
211     return gregorian::Jan;
212   else if (str == _("feb") || str == _("february") || str == "1")
213     return gregorian::Feb;
214   else if (str == _("mar") || str == _("march") || str == "2")
215     return gregorian::Mar;
216   else if (str == _("apr") || str == _("april") || str == "3")
217     return gregorian::Apr;
218   else if (str == _("may") || str == _("may") || str == "4")
219     return gregorian::May;
220   else if (str == _("jun") || str == _("june") || str == "5")
221     return gregorian::Jun;
222   else if (str == _("jul") || str == _("july") || str == "6")
223     return gregorian::Jul;
224   else if (str == _("aug") || str == _("august") || str == "7")
225     return gregorian::Aug;
226   else if (str == _("sep") || str == _("september") || str == "8")
227     return gregorian::Sep;
228   else if (str == _("oct") || str == _("october") || str == "9")
229     return gregorian::Oct;
230   else if (str == _("nov") || str == _("november") || str == "10")
231     return gregorian::Nov;
232   else if (str == _("dec") || str == _("december") || str == "11")
233     return gregorian::Dec;
234   else
235     return none;
236 }
237 
parse_datetime(const char * str)238 datetime_t parse_datetime(const char * str)
239 {
240   if (std::strlen(str) > 127) {
241     throw_(date_error, _f("Invalid date: %1%") % str);
242   }
243 
244   char buf[128];
245   std::strcpy(buf, str);
246 
247   for (char * p = buf; *p; p++)
248     if (*p == '.' || *p == '-')
249       *p = '/';
250 
251   datetime_t when = input_datetime_io->parse(buf);
252   if (when.is_not_a_date_time()) {
253     when = timelog_datetime_io->parse(buf);
254     if (when.is_not_a_date_time()) {
255       throw_(date_error, _f("Invalid date/time: %1%") % str);
256     }
257   }
258   return when;
259 }
260 
parse_date(const char * str)261 date_t parse_date(const char * str)
262 {
263   return parse_date_mask(str);
264 }
265 
begin() const266 date_t date_specifier_t::begin() const
267 {
268   year_type  the_year  = year  ? *year  : year_type(CURRENT_DATE().year());
269   month_type the_month = month ? *month : date_t::month_type(1);
270   day_type   the_day   = day   ? *day   : date_t::day_type(1);
271 
272 #if !NO_ASSERTS
273   if (day)
274     assert(! wday);
275   else if (wday)
276     assert(! day);
277 #endif
278 
279   // jww (2009-11-16): Handle wday.  If a month is set, find the most recent
280   // wday in that month; if the year is set, then in that year.
281 
282   return gregorian::date(static_cast<date_t::year_type>(the_year),
283                          static_cast<date_t::month_type>(the_month),
284                          static_cast<date_t::day_type>(the_day));
285 }
286 
end() const287 date_t date_specifier_t::end() const
288 {
289   if (day || wday)
290     return begin() + gregorian::days(1);
291   else if (month)
292     return begin() + gregorian::months(1);
293   else if (year)
294     return begin() + gregorian::years(1);
295   else {
296     assert(false);
297     return date_t();
298   }
299 }
300 
operator <<(std::ostream & out,const date_duration_t & duration)301 std::ostream& operator<<(std::ostream& out,
302                          const date_duration_t& duration)
303 {
304   if (duration.quantum == date_duration_t::DAYS)
305     out << duration.length << " day(s)";
306   else if (duration.quantum == date_duration_t::WEEKS)
307     out << duration.length << " week(s)";
308   else if (duration.quantum == date_duration_t::MONTHS)
309     out << duration.length << " month(s)";
310   else if (duration.quantum == date_duration_t::QUARTERS)
311     out << duration.length << " quarter(s)";
312   else {
313     assert(duration.quantum == date_duration_t::YEARS);
314     out << duration.length << " year(s)";
315   }
316   return out;
317 }
318 
319 class date_parser_t
320 {
321   friend void show_period_tokens(std::ostream& out, const string& arg);
322 
323   class lexer_t
324   {
325     friend class date_parser_t;
326 
327     string::const_iterator begin;
328     string::const_iterator end;
329 
330   public:
331     struct token_t
332     {
333       enum kind_t {
334         UNKNOWN,
335 
336         TOK_DATE,
337         TOK_INT,
338         TOK_SLASH,
339         TOK_DASH,
340         TOK_DOT,
341 
342         TOK_A_MONTH,
343         TOK_A_WDAY,
344 
345         TOK_AGO,
346         TOK_HENCE,
347         TOK_SINCE,
348         TOK_UNTIL,
349         TOK_IN,
350         TOK_THIS,
351         TOK_NEXT,
352         TOK_LAST,
353         TOK_EVERY,
354 
355         TOK_TODAY,
356         TOK_TOMORROW,
357         TOK_YESTERDAY,
358 
359         TOK_YEAR,
360         TOK_QUARTER,
361         TOK_MONTH,
362         TOK_WEEK,
363         TOK_DAY,
364 
365         TOK_YEARLY,
366         TOK_QUARTERLY,
367         TOK_BIMONTHLY,
368         TOK_MONTHLY,
369         TOK_BIWEEKLY,
370         TOK_WEEKLY,
371         TOK_DAILY,
372 
373         TOK_YEARS,
374         TOK_QUARTERS,
375         TOK_MONTHS,
376         TOK_WEEKS,
377         TOK_DAYS,
378 
379         END_REACHED
380 
381       } kind;
382 
383       typedef variant<unsigned short,
384                       string,
385                       date_specifier_t::year_type,
386                       date_time::months_of_year,
387                       date_time::weekdays,
388                       date_specifier_t> content_t;
389 
390       optional<content_t> value;
391 
token_tledger::date_parser_t::lexer_t::token_t392       explicit token_t(kind_t _kind = UNKNOWN,
393                        const optional<content_t>& _value =
394                        content_t(empty_string))
395         : kind(_kind), value(_value) {
396         TRACE_CTOR(date_parser_t::lexer_t::token_t, "");
397       }
token_tledger::date_parser_t::lexer_t::token_t398       token_t(const token_t& tok)
399         : kind(tok.kind), value(tok.value) {
400         TRACE_CTOR(date_parser_t::lexer_t::token_t, "copy");
401       }
~token_tledger::date_parser_t::lexer_t::token_t402       ~token_t() throw() {
403         TRACE_DTOR(date_parser_t::lexer_t::token_t);
404       }
405 
operator =ledger::date_parser_t::lexer_t::token_t406       token_t& operator=(const token_t& tok) {
407         if (this != &tok) {
408           kind  = tok.kind;
409           value = tok.value;
410         }
411         return *this;
412       }
413 
operator boolledger::date_parser_t::lexer_t::token_t414       operator bool() const {
415         return kind != END_REACHED;
416       }
417 
to_stringledger::date_parser_t::lexer_t::token_t418       string to_string() const {
419         std::ostringstream out;
420 
421         switch (kind) {
422         case UNKNOWN:
423           out << boost::get<string>(*value);
424           break;
425         case TOK_DATE:
426           return boost::get<date_specifier_t>(*value).to_string();
427         case TOK_INT:
428           out << boost::get<unsigned short>(*value);
429           break;
430         case TOK_SLASH:     return "/";
431         case TOK_DASH:      return "-";
432         case TOK_DOT:       return ".";
433         case TOK_A_MONTH:
434           out << date_specifier_t::month_type
435             (boost::get<date_time::months_of_year>(*value));
436           break;
437         case TOK_A_WDAY:
438           out << date_specifier_t::day_of_week_type
439             (boost::get<date_time::weekdays>(*value));
440           break;
441         case TOK_AGO:       return "ago";
442         case TOK_HENCE:     return "hence";
443         case TOK_SINCE:     return "since";
444         case TOK_UNTIL:     return "until";
445         case TOK_IN:        return "in";
446         case TOK_THIS:      return "this";
447         case TOK_NEXT:      return "next";
448         case TOK_LAST:      return "last";
449         case TOK_EVERY:     return "every";
450         case TOK_TODAY:     return "today";
451         case TOK_TOMORROW:  return "tomorrow";
452         case TOK_YESTERDAY: return "yesterday";
453         case TOK_YEAR:      return "year";
454         case TOK_QUARTER:   return "quarter";
455         case TOK_MONTH:     return "month";
456         case TOK_WEEK:      return "week";
457         case TOK_DAY:       return "day";
458         case TOK_YEARLY:    return "yearly";
459         case TOK_QUARTERLY: return "quarterly";
460         case TOK_BIMONTHLY: return "bimonthly";
461         case TOK_MONTHLY:   return "monthly";
462         case TOK_BIWEEKLY:  return "biweekly";
463         case TOK_WEEKLY:    return "weekly";
464         case TOK_DAILY:     return "daily";
465         case TOK_YEARS:     return "years";
466         case TOK_QUARTERS:  return "quarters";
467         case TOK_MONTHS:    return "months";
468         case TOK_WEEKS:     return "weeks";
469         case TOK_DAYS:      return "days";
470         case END_REACHED:   return "<EOF>";
471         }
472 
473         return out.str();
474       }
475 
dumpledger::date_parser_t::lexer_t::token_t476       void dump(std::ostream& out) const {
477         switch (kind) {
478         case UNKNOWN:       out << "UNKNOWN"; break;
479         case TOK_DATE:      out << "TOK_DATE"; break;
480         case TOK_INT:       out << "TOK_INT"; break;
481         case TOK_SLASH:     out << "TOK_SLASH"; break;
482         case TOK_DASH:      out << "TOK_DASH"; break;
483         case TOK_DOT:       out << "TOK_DOT"; break;
484         case TOK_A_MONTH:   out << "TOK_A_MONTH"; break;
485         case TOK_A_WDAY:    out << "TOK_A_WDAY"; break;
486         case TOK_AGO:       out << "TOK_AGO"; break;
487         case TOK_HENCE:     out << "TOK_HENCE"; break;
488         case TOK_SINCE:     out << "TOK_SINCE"; break;
489         case TOK_UNTIL:     out << "TOK_UNTIL"; break;
490         case TOK_IN:        out << "TOK_IN"; break;
491         case TOK_THIS:      out << "TOK_THIS"; break;
492         case TOK_NEXT:      out << "TOK_NEXT"; break;
493         case TOK_LAST:      out << "TOK_LAST"; break;
494         case TOK_EVERY:     out << "TOK_EVERY"; break;
495         case TOK_TODAY:     out << "TOK_TODAY"; break;
496         case TOK_TOMORROW:  out << "TOK_TOMORROW"; break;
497         case TOK_YESTERDAY: out << "TOK_YESTERDAY"; break;
498         case TOK_YEAR:      out << "TOK_YEAR"; break;
499         case TOK_QUARTER:   out << "TOK_QUARTER"; break;
500         case TOK_MONTH:     out << "TOK_MONTH"; break;
501         case TOK_WEEK:      out << "TOK_WEEK"; break;
502         case TOK_DAY:       out << "TOK_DAY"; break;
503         case TOK_YEARLY:    out << "TOK_YEARLY"; break;
504         case TOK_QUARTERLY: out << "TOK_QUARTERLY"; break;
505         case TOK_BIMONTHLY: out << "TOK_BIMONTHLY"; break;
506         case TOK_MONTHLY:   out << "TOK_MONTHLY"; break;
507         case TOK_BIWEEKLY:  out << "TOK_BIWEEKLY"; break;
508         case TOK_WEEKLY:    out << "TOK_WEEKLY"; break;
509         case TOK_DAILY:     out << "TOK_DAILY"; break;
510         case TOK_YEARS:     out << "TOK_YEARS"; break;
511         case TOK_QUARTERS:  out << "TOK_QUARTERS"; break;
512         case TOK_MONTHS:    out << "TOK_MONTHS"; break;
513         case TOK_WEEKS:     out << "TOK_WEEKS"; break;
514         case TOK_DAYS:      out << "TOK_DAYS"; break;
515         case END_REACHED:   out << "END_REACHED"; break;
516         }
517       }
518 
519       void unexpected();
520       static void expected(char wanted, char c = '\0');
521     };
522 
523     token_t token_cache;
524 
lexer_t(string::const_iterator _begin,string::const_iterator _end)525     lexer_t(string::const_iterator _begin,
526             string::const_iterator _end)
527       : begin(_begin), end(_end)
528     {
529       TRACE_CTOR(date_parser_t::lexer_t, "");
530     }
lexer_t(const lexer_t & other)531     lexer_t(const lexer_t& other)
532       : begin(other.begin), end(other.end),
533         token_cache(other.token_cache)
534     {
535       TRACE_CTOR(date_parser_t::lexer_t, "copy");
536     }
~lexer_t()537     ~lexer_t() throw() {
538       TRACE_DTOR(date_parser_t::lexer_t);
539     }
540 
541     token_t next_token();
push_token(token_t tok)542     void    push_token(token_t tok) {
543       assert(token_cache.kind == token_t::UNKNOWN);
544       token_cache = tok;
545     }
peek_token()546     token_t peek_token() {
547       if (token_cache.kind == token_t::UNKNOWN)
548         token_cache = next_token();
549       return token_cache;
550     }
551   };
552 
553   string  arg;
554   lexer_t lexer;
555 
556 public:
date_parser_t(const string & _arg)557   date_parser_t(const string& _arg)
558     : arg(_arg), lexer(arg.begin(), arg.end()) {
559     TRACE_CTOR(date_parser_t, "");
560   }
date_parser_t(const date_parser_t & parser)561   date_parser_t(const date_parser_t& parser)
562     : arg(parser.arg), lexer(parser.lexer) {
563     TRACE_CTOR(date_parser_t, "copy");
564   }
~date_parser_t()565   ~date_parser_t() throw() {
566     TRACE_DTOR(date_parser_t);
567   }
568 
569   date_interval_t parse();
570 
571 private:
572   void determine_when(lexer_t::token_t& tok, date_specifier_t& specifier);
573 };
574 
determine_when(date_parser_t::lexer_t::token_t & tok,date_specifier_t & specifier)575 void date_parser_t::determine_when(date_parser_t::lexer_t::token_t& tok,
576                                    date_specifier_t& specifier)
577 {
578   date_t today = CURRENT_DATE();
579 
580   switch (tok.kind) {
581   case lexer_t::token_t::TOK_DATE:
582     specifier = boost::get<date_specifier_t>(*tok.value);
583     break;
584 
585   case lexer_t::token_t::TOK_INT: {
586     unsigned short amount = boost::get<unsigned short>(*tok.value);
587     int8_t         adjust = 0;
588 
589     tok = lexer.peek_token();
590     lexer_t::token_t::kind_t kind = tok.kind;
591     switch (kind) {
592     case lexer_t::token_t::TOK_YEAR:
593     case lexer_t::token_t::TOK_YEARS:
594     case lexer_t::token_t::TOK_QUARTER:
595     case lexer_t::token_t::TOK_QUARTERS:
596     case lexer_t::token_t::TOK_MONTH:
597     case lexer_t::token_t::TOK_MONTHS:
598     case lexer_t::token_t::TOK_WEEK:
599     case lexer_t::token_t::TOK_WEEKS:
600     case lexer_t::token_t::TOK_DAY:
601     case lexer_t::token_t::TOK_DAYS:
602       lexer.next_token();
603       tok = lexer.next_token();
604       switch (tok.kind) {
605       case lexer_t::token_t::TOK_AGO:
606         adjust = -1;
607         break;
608       case lexer_t::token_t::TOK_HENCE:
609         adjust = 1;
610         break;
611       default:
612         tok.unexpected();
613         break;
614       }
615       break;
616     default:
617       break;
618     }
619 
620     date_t when(today);
621 
622     switch (kind) {
623     case lexer_t::token_t::TOK_YEAR:
624     case lexer_t::token_t::TOK_YEARS:
625       when += gregorian::years(amount * adjust);
626       break;
627     case lexer_t::token_t::TOK_QUARTER:
628     case lexer_t::token_t::TOK_QUARTERS:
629       when += gregorian::months(amount * 3 * adjust);
630       break;
631     case lexer_t::token_t::TOK_MONTH:
632     case lexer_t::token_t::TOK_MONTHS:
633       when += gregorian::months(amount * adjust);
634       break;
635     case lexer_t::token_t::TOK_WEEK:
636     case lexer_t::token_t::TOK_WEEKS:
637       when += gregorian::weeks(amount * adjust);
638       break;
639     case lexer_t::token_t::TOK_DAY:
640     case lexer_t::token_t::TOK_DAYS:
641       when += gregorian::days(amount * adjust);
642       break;
643     default:
644       if (amount > 31) {
645         specifier.year = date_specifier_t::year_type(amount);
646       } else {
647         specifier.day = date_specifier_t::day_type(amount);
648       }
649       break;
650     }
651 
652     if (adjust)
653       specifier = date_specifier_t(when);
654     break;
655   }
656 
657   case lexer_t::token_t::TOK_THIS:
658   case lexer_t::token_t::TOK_NEXT:
659   case lexer_t::token_t::TOK_LAST: {
660     int8_t adjust = 0;
661     if (tok.kind == lexer_t::token_t::TOK_NEXT)
662       adjust = 1;
663     else if (tok.kind == lexer_t::token_t::TOK_LAST)
664       adjust = -1;
665 
666     tok = lexer.next_token();
667     switch (tok.kind) {
668     case lexer_t::token_t::TOK_A_MONTH: {
669       date_t temp(today.year(),
670                   boost::get<date_time::months_of_year>(*tok.value), 1);
671       temp += gregorian::years(adjust);
672       specifier =
673         date_specifier_t(static_cast<date_specifier_t::year_type>(temp.year()),
674                          temp.month());
675       break;
676     }
677 
678     case lexer_t::token_t::TOK_A_WDAY: {
679       date_t temp =
680         date_duration_t::find_nearest(today, date_duration_t::WEEKS);
681       while (temp.day_of_week() !=
682              boost::get<date_time::months_of_year>(*tok.value))
683         temp += gregorian::days(1);
684       temp += gregorian::days(7 * adjust);
685       specifier = date_specifier_t(temp);
686       break;
687     }
688 
689     case lexer_t::token_t::TOK_YEAR: {
690       date_t temp(today);
691       temp += gregorian::years(adjust);
692       specifier =
693         date_specifier_t(static_cast<date_specifier_t::year_type>(temp.year()));
694       break;
695     }
696 
697     case lexer_t::token_t::TOK_QUARTER: {
698       date_t base =
699         date_duration_t::find_nearest(today, date_duration_t::QUARTERS);
700       date_t temp;
701       if (adjust < 0) {
702         temp = base + gregorian::months(3 * adjust);
703       }
704       else if (adjust == 0) {
705         temp = base + gregorian::months(3);
706       }
707       else if (adjust > 0) {
708         base += gregorian::months(3 * adjust);
709         temp = base + gregorian::months(3 * adjust);
710       }
711       specifier = date_specifier_t(adjust < 0 ? temp : base);
712       break;
713     }
714 
715     case lexer_t::token_t::TOK_WEEK: {
716       date_t base =
717         date_duration_t::find_nearest(today, date_duration_t::WEEKS);
718       date_t temp;
719       if (adjust < 0) {
720         temp = base + gregorian::days(7 * adjust);
721       }
722       else if (adjust == 0) {
723         temp = base + gregorian::days(7);
724       }
725       else if (adjust > 0) {
726         base += gregorian::days(7 * adjust);
727         temp = base + gregorian::days(7 * adjust);
728       }
729       specifier = date_specifier_t(adjust < 0 ? temp : base);
730       break;
731     }
732 
733     case lexer_t::token_t::TOK_DAY: {
734       date_t temp(today);
735       temp += gregorian::days(adjust);
736       specifier = date_specifier_t(temp);
737       break;
738     }
739 
740     default:
741     case lexer_t::token_t::TOK_MONTH: {
742       date_t temp(today);
743       temp += gregorian::months(adjust);
744       specifier =
745         date_specifier_t(static_cast<date_specifier_t::year_type>(temp.year()),
746                          temp.month());
747       break;
748     }
749     }
750     break;
751   }
752 
753   case lexer_t::token_t::TOK_A_MONTH:
754     specifier.month =
755       date_specifier_t::month_type
756         (boost::get<date_time::months_of_year>(*tok.value));
757     tok = lexer.peek_token();
758     switch (tok.kind) {
759     case lexer_t::token_t::TOK_INT:
760       specifier.year = boost::get<date_specifier_t::year_type>(*tok.value);
761       break;
762     case lexer_t::token_t::END_REACHED:
763       break;
764     default:
765       break;
766     }
767     break;
768   case lexer_t::token_t::TOK_A_WDAY:
769     specifier.wday  =
770       date_specifier_t::day_of_week_type
771         (boost::get<date_time::weekdays>(*tok.value));
772     break;
773 
774   case lexer_t::token_t::TOK_TODAY:
775     specifier = date_specifier_t(today);
776     break;
777   case lexer_t::token_t::TOK_TOMORROW:
778     specifier = date_specifier_t(today + gregorian::days(1));
779     break;
780   case lexer_t::token_t::TOK_YESTERDAY:
781     specifier = date_specifier_t(today - gregorian::days(1));
782     break;
783 
784   default:
785     tok.unexpected();
786     break;
787   }
788 }
789 
parse()790 date_interval_t date_parser_t::parse()
791 {
792   optional<date_specifier_t> since_specifier;
793   optional<date_specifier_t> until_specifier;
794   optional<date_specifier_t> inclusion_specifier;
795 
796   date_interval_t period;
797   date_t          today = CURRENT_DATE();
798   bool            end_inclusive = false;
799 
800   for (lexer_t::token_t tok = lexer.next_token();
801        tok.kind != lexer_t::token_t::END_REACHED;
802        tok = lexer.next_token()) {
803     switch (tok.kind) {
804     case lexer_t::token_t::TOK_DATE:
805       if (! inclusion_specifier)
806         inclusion_specifier = date_specifier_t();
807       determine_when(tok, *inclusion_specifier);
808       break;
809 
810     case lexer_t::token_t::TOK_INT:
811       if (! inclusion_specifier)
812         inclusion_specifier = date_specifier_t();
813       determine_when(tok, *inclusion_specifier);
814       break;
815 
816     case lexer_t::token_t::TOK_A_MONTH:
817       if (! inclusion_specifier)
818         inclusion_specifier = date_specifier_t();
819       determine_when(tok, *inclusion_specifier);
820       break;
821 
822     case lexer_t::token_t::TOK_A_WDAY:
823       if (! inclusion_specifier)
824         inclusion_specifier = date_specifier_t();
825       determine_when(tok, *inclusion_specifier);
826       break;
827 
828     case lexer_t::token_t::TOK_DASH:
829       if (inclusion_specifier) {
830         since_specifier     = inclusion_specifier;
831         until_specifier     = date_specifier_t();
832         inclusion_specifier = none;
833 
834         tok = lexer.next_token();
835         determine_when(tok, *until_specifier);
836 
837         // The dash operator is special: it has an _inclusive_ end.
838         end_inclusive = true;
839       } else {
840         tok.unexpected();
841       }
842       break;
843 
844     case lexer_t::token_t::TOK_SINCE:
845       if (since_specifier) {
846         tok.unexpected();
847       } else {
848         since_specifier = date_specifier_t();
849         tok = lexer.next_token();
850         determine_when(tok, *since_specifier);
851       }
852       break;
853 
854     case lexer_t::token_t::TOK_UNTIL:
855       if (until_specifier) {
856         tok.unexpected();
857       } else {
858         until_specifier = date_specifier_t();
859         tok = lexer.next_token();
860         determine_when(tok, *until_specifier);
861       }
862       break;
863 
864     case lexer_t::token_t::TOK_IN:
865       if (inclusion_specifier) {
866         tok.unexpected();
867       } else {
868         inclusion_specifier = date_specifier_t();
869         tok = lexer.next_token();
870         determine_when(tok, *inclusion_specifier);
871       }
872       break;
873 
874     case lexer_t::token_t::TOK_THIS:
875     case lexer_t::token_t::TOK_NEXT:
876     case lexer_t::token_t::TOK_LAST: {
877       int8_t adjust = 0;
878       if (tok.kind == lexer_t::token_t::TOK_NEXT)
879         adjust = 1;
880       else if (tok.kind == lexer_t::token_t::TOK_LAST)
881         adjust = -1;
882 
883       tok = lexer.next_token();
884       switch (tok.kind) {
885       case lexer_t::token_t::TOK_INT: {
886         unsigned short amount = boost::get<unsigned short>(*tok.value);
887 
888         date_t base(today);
889         date_t end(today);
890 
891         if (! adjust)
892           adjust = 1;
893 
894         tok = lexer.next_token();
895         switch (tok.kind) {
896         case lexer_t::token_t::TOK_YEARS:
897           base += gregorian::years(amount * adjust);
898           break;
899         case lexer_t::token_t::TOK_QUARTERS:
900           base += gregorian::months(amount * adjust * 3);
901           break;
902         case lexer_t::token_t::TOK_MONTHS:
903           base += gregorian::months(amount * adjust);
904           break;
905         case lexer_t::token_t::TOK_WEEKS:
906           base += gregorian::weeks(amount * adjust);
907           break;
908         case lexer_t::token_t::TOK_DAYS:
909           base += gregorian::days(amount * adjust);
910           break;
911         default:
912           tok.unexpected();
913           break;
914         }
915 
916         if (adjust >= 0) {
917           date_t temp = base;
918           base = end;
919           end = temp;
920         }
921 
922         since_specifier = date_specifier_t(base);
923         until_specifier = date_specifier_t(end);
924         break;
925       }
926 
927       case lexer_t::token_t::TOK_A_MONTH: {
928         inclusion_specifier = date_specifier_t();
929         determine_when(tok, *inclusion_specifier);
930 
931         date_t temp(today.year(), *inclusion_specifier->month, 1);
932         temp += gregorian::years(adjust);
933         inclusion_specifier =
934           date_specifier_t(static_cast<date_specifier_t::year_type>(temp.year()),
935                            temp.month());
936         break;
937       }
938 
939       case lexer_t::token_t::TOK_A_WDAY: {
940         inclusion_specifier = date_specifier_t();
941         determine_when(tok, *inclusion_specifier);
942 
943         date_t temp =
944           date_duration_t::find_nearest(today, date_duration_t::WEEKS);
945         while (temp.day_of_week() != inclusion_specifier->wday)
946           temp += gregorian::days(1);
947         temp += gregorian::days(7 * adjust);
948         inclusion_specifier = date_specifier_t(temp);
949         break;
950       }
951 
952       case lexer_t::token_t::TOK_YEAR: {
953         date_t temp(today);
954         temp += gregorian::years(adjust);
955         inclusion_specifier =
956           date_specifier_t(static_cast<date_specifier_t::year_type>(temp.year()));
957         break;
958       }
959 
960       case lexer_t::token_t::TOK_QUARTER: {
961         date_t base =
962           date_duration_t::find_nearest(today, date_duration_t::QUARTERS);
963         date_t temp;
964         if (adjust < 0) {
965           temp = base + gregorian::months(3 * adjust);
966         }
967         else if (adjust == 0) {
968           temp = base + gregorian::months(3);
969         }
970         else if (adjust > 0) {
971           base += gregorian::months(3 * adjust);
972           temp = base + gregorian::months(3 * adjust);
973         }
974         since_specifier = date_specifier_t(adjust < 0 ? temp : base);
975         until_specifier = date_specifier_t(adjust < 0 ? base : temp);
976         break;
977       }
978 
979       case lexer_t::token_t::TOK_WEEK: {
980         date_t base =
981           date_duration_t::find_nearest(today, date_duration_t::WEEKS);
982         date_t temp;
983         if (adjust < 0) {
984           temp = base + gregorian::days(7 * adjust);
985         }
986         else if (adjust == 0) {
987           temp = base + gregorian::days(7);
988         }
989         else if (adjust > 0) {
990           base += gregorian::days(7 * adjust);
991           temp = base + gregorian::days(7 * adjust);
992         }
993         since_specifier = date_specifier_t(adjust < 0 ? temp : base);
994         until_specifier = date_specifier_t(adjust < 0 ? base : temp);
995         break;
996       }
997 
998       case lexer_t::token_t::TOK_DAY: {
999         date_t temp(today);
1000         temp += gregorian::days(adjust);
1001         inclusion_specifier = date_specifier_t(temp);
1002         break;
1003       }
1004 
1005       default:
1006       case lexer_t::token_t::TOK_MONTH: {
1007         date_t temp(today);
1008         temp += gregorian::months(adjust);
1009         inclusion_specifier =
1010           date_specifier_t(static_cast<date_specifier_t::year_type>(temp.year()),
1011                            temp.month());
1012         break;
1013       }
1014       }
1015       break;
1016     }
1017 
1018     case lexer_t::token_t::TOK_TODAY:
1019       inclusion_specifier = date_specifier_t(today);
1020       break;
1021     case lexer_t::token_t::TOK_TOMORROW:
1022       inclusion_specifier = date_specifier_t(today + gregorian::days(1));
1023       break;
1024     case lexer_t::token_t::TOK_YESTERDAY:
1025       inclusion_specifier = date_specifier_t(today - gregorian::days(1));
1026       break;
1027 
1028     case lexer_t::token_t::TOK_EVERY:
1029       tok = lexer.next_token();
1030       if (tok.kind == lexer_t::token_t::TOK_INT) {
1031         int quantity = boost::get<unsigned short>(*tok.value);
1032         tok = lexer.next_token();
1033         switch (tok.kind) {
1034         case lexer_t::token_t::TOK_YEARS:
1035           period.duration = date_duration_t(date_duration_t::YEARS, quantity);
1036           break;
1037         case lexer_t::token_t::TOK_QUARTERS:
1038           period.duration = date_duration_t(date_duration_t::QUARTERS, quantity);
1039           break;
1040         case lexer_t::token_t::TOK_MONTHS:
1041           period.duration = date_duration_t(date_duration_t::MONTHS, quantity);
1042           break;
1043         case lexer_t::token_t::TOK_WEEKS:
1044           period.duration = date_duration_t(date_duration_t::WEEKS, quantity);
1045           break;
1046         case lexer_t::token_t::TOK_DAYS:
1047           period.duration = date_duration_t(date_duration_t::DAYS, quantity);
1048           break;
1049         default:
1050           tok.unexpected();
1051           break;
1052         }
1053       } else {
1054         switch (tok.kind) {
1055         case lexer_t::token_t::TOK_YEAR:
1056           period.duration = date_duration_t(date_duration_t::YEARS, 1);
1057           break;
1058         case lexer_t::token_t::TOK_QUARTER:
1059           period.duration = date_duration_t(date_duration_t::QUARTERS, 1);
1060           break;
1061         case lexer_t::token_t::TOK_MONTH:
1062           period.duration = date_duration_t(date_duration_t::MONTHS, 1);
1063           break;
1064         case lexer_t::token_t::TOK_WEEK:
1065           period.duration = date_duration_t(date_duration_t::WEEKS, 1);
1066           break;
1067         case lexer_t::token_t::TOK_DAY:
1068           period.duration = date_duration_t(date_duration_t::DAYS, 1);
1069           break;
1070         default:
1071           tok.unexpected();
1072           break;
1073         }
1074       }
1075       break;
1076 
1077     case lexer_t::token_t::TOK_YEARLY:
1078       period.duration = date_duration_t(date_duration_t::YEARS, 1);
1079       break;
1080     case lexer_t::token_t::TOK_QUARTERLY:
1081       period.duration = date_duration_t(date_duration_t::QUARTERS, 1);
1082       break;
1083     case lexer_t::token_t::TOK_BIMONTHLY:
1084       period.duration = date_duration_t(date_duration_t::MONTHS, 2);
1085       break;
1086     case lexer_t::token_t::TOK_MONTHLY:
1087       period.duration = date_duration_t(date_duration_t::MONTHS, 1);
1088       break;
1089     case lexer_t::token_t::TOK_BIWEEKLY:
1090       period.duration = date_duration_t(date_duration_t::WEEKS, 2);
1091       break;
1092     case lexer_t::token_t::TOK_WEEKLY:
1093       period.duration = date_duration_t(date_duration_t::WEEKS, 1);
1094       break;
1095     case lexer_t::token_t::TOK_DAILY:
1096       period.duration = date_duration_t(date_duration_t::DAYS, 1);
1097       break;
1098 
1099     default:
1100       tok.unexpected();
1101       break;
1102     }
1103   }
1104 
1105 #if 0
1106   if (! period.duration && inclusion_specifier)
1107     period.duration = inclusion_specifier->implied_duration();
1108 #endif
1109 
1110   if (since_specifier || until_specifier) {
1111     date_range_t range(since_specifier, until_specifier);
1112     range.end_inclusive = end_inclusive;
1113 
1114     period.range = date_specifier_or_range_t(range);
1115   }
1116   else if (inclusion_specifier) {
1117     period.range = date_specifier_or_range_t(*inclusion_specifier);
1118   }
1119   else {
1120     /* otherwise, it's something like "monthly", with no date reference */
1121   }
1122 
1123   return period;
1124 }
1125 
parse(const string & str)1126 void date_interval_t::parse(const string& str)
1127 {
1128   date_parser_t parser(str);
1129   *this = parser.parse();
1130 }
1131 
resolve_end()1132 void date_interval_t::resolve_end()
1133 {
1134   if (start && ! end_of_duration) {
1135     end_of_duration = duration->add(*start);
1136     DEBUG("times.interval",
1137           "stabilize: end_of_duration = " << *end_of_duration);
1138   }
1139 
1140   if (finish && *end_of_duration > *finish) {
1141     end_of_duration = finish;
1142     DEBUG("times.interval",
1143           "stabilize: end_of_duration reset to end: " << *end_of_duration);
1144   }
1145 
1146   if (start && ! next) {
1147     next = end_of_duration;
1148     DEBUG("times.interval", "stabilize: next set to: " << *next);
1149   }
1150 }
1151 
find_nearest(const date_t & date,skip_quantum_t skip)1152 date_t date_duration_t::find_nearest(const date_t& date, skip_quantum_t skip)
1153 {
1154   date_t result;
1155 
1156   switch (skip) {
1157   case date_duration_t::YEARS:
1158     result = date_t(date.year(), gregorian::Jan, 1);
1159     break;
1160   case date_duration_t::QUARTERS:
1161     result = date_t(date.year(), date.month(), 1);
1162     while (result.month() != gregorian::Jan &&
1163            result.month() != gregorian::Apr &&
1164            result.month() != gregorian::Jul &&
1165            result.month() != gregorian::Oct)
1166       result -= gregorian::months(1);
1167     break;
1168   case date_duration_t::MONTHS:
1169     result = date_t(date.year(), date.month(), 1);
1170     break;
1171   case date_duration_t::WEEKS:
1172     result = date;
1173     while (result.day_of_week() != start_of_week)
1174       result -= gregorian::days(1);
1175     break;
1176   case date_duration_t::DAYS:
1177     result = date;
1178     break;
1179   }
1180   return result;
1181 }
1182 
stabilize(const optional<date_t> & date)1183 void date_interval_t::stabilize(const optional<date_t>& date)
1184 {
1185 #if DEBUG_ON
1186   if (date)
1187     DEBUG("times.interval", "stabilize: with date = " << *date);
1188 #endif
1189 
1190   if (date && ! aligned) {
1191     DEBUG("times.interval", "stabilize: date passed, but not aligned");
1192     if (duration) {
1193       DEBUG("times.interval",
1194             "stabilize: aligning with a duration: " << *duration);
1195 
1196       // The interval object has not been seeded with a start date yet, so
1197       // find the nearest period before or on date which fits, if possible.
1198       //
1199       // Find an efficient starting point for the upcoming while loop.  We
1200       // want a date early enough that the range will be correct, but late
1201       // enough that we don't spend hundreds of thousands of loops skipping
1202       // through time.
1203       optional<date_t> initial_start  = start  ? start  : begin();
1204       optional<date_t> initial_finish = finish ? finish : end();
1205 
1206 #if DEBUG_ON
1207       if (initial_start)
1208         DEBUG("times.interval",
1209               "stabilize: initial_start  = " << *initial_start);
1210       if (initial_finish)
1211         DEBUG("times.interval",
1212               "stabilize: initial_finish = " << *initial_finish);
1213 #endif
1214 
1215       date_t when = start ? *start : *date;
1216       switch (duration->quantum) {
1217       case date_duration_t::MONTHS:
1218       case date_duration_t::QUARTERS:
1219       case date_duration_t::YEARS:
1220         // These start on most recent period start quantum before when
1221         DEBUG("times.interval",
1222             "stabilize: monthly, quarterly or yearly duration");
1223         start = date_duration_t::find_nearest(when, duration->quantum);
1224         break;
1225       case date_duration_t::WEEKS:
1226         // Weeks start on the beginning of week prior to 400 remainder period length
1227         // Either the first quanta of the period or the last quanta of the period seems more sensible
1228         // implies period is never less than 400 days not too unreasonable
1229         DEBUG("times.interval", "stabilize: weekly duration");
1230         {
1231           int period = duration->length * 7;
1232           start = date_duration_t::find_nearest(
1233               when - gregorian::days(period + 400 % period), duration->quantum);
1234         }
1235         break;
1236       default:
1237         // multiples of days have a quanta of 1 day so should not have the start date adjusted to a quanta
1238         DEBUG("times.interval",
1239             "stabilize: daily duration - stable by definition");
1240         start = when;
1241         break;
1242       }
1243 
1244       DEBUG("times.interval", "stabilize: beginning start date = " << *start);
1245 
1246       while (*start < *date) {
1247         date_interval_t next_interval(*this);
1248         ++next_interval;
1249 
1250         if (next_interval.start && *next_interval.start <= *date) {
1251           *this = next_interval;
1252         } else {
1253           end_of_duration = none;
1254           next = none;
1255           break;
1256         }
1257       }
1258 
1259       DEBUG("times.interval", "stabilize: proposed start date = " << *start);
1260 
1261       if (initial_start && (! start || *start < *initial_start)) {
1262         // Using the discovered start, find the end of the period
1263         resolve_end();
1264 
1265         start = initial_start;
1266         DEBUG("times.interval", "stabilize: start reset to initial start");
1267       }
1268       if (initial_finish && (! finish || *finish > *initial_finish)) {
1269         finish = initial_finish;
1270         DEBUG("times.interval", "stabilize: finish reset to initial finish");
1271       }
1272 
1273 #if DEBUG_ON
1274       if (start)
1275         DEBUG("times.interval", "stabilize: final start  = " << *start);
1276       if (finish)
1277         DEBUG("times.interval", "stabilize: final finish = " << *finish);
1278 #endif
1279     }
1280     else if (range) {
1281       start  = range->begin();
1282       finish = range->end();
1283     }
1284     aligned = true;
1285   }
1286 
1287   // If there is no duration, then if we've reached here the date falls
1288   // between start and finish.
1289   if (! duration) {
1290     DEBUG("times.interval", "stabilize: there was no duration given");
1291 
1292     if (! start && ! finish)
1293       throw_(date_error,
1294              _("Invalid date interval: neither start, nor finish, nor duration"));
1295   } else {
1296     resolve_end();
1297   }
1298 }
1299 
find_period(const date_t & date,const bool allow_shift)1300 bool date_interval_t::find_period(const date_t& date,
1301                                   const bool    allow_shift)
1302 {
1303   stabilize(date);
1304 
1305   if (finish && date > *finish) {
1306     DEBUG("times.interval",
1307           "false: date [" << date << "] > finish [" << *finish << "]");
1308     return false;
1309   }
1310 
1311   if (! start) {
1312     throw_(std::runtime_error, _("Date interval is improperly initialized"));
1313   }
1314   else if (date < *start) {
1315     DEBUG("times.interval",
1316           "false: date [" << date << "] < start [" << *start << "]");
1317     return false;
1318   }
1319 
1320   if (end_of_duration) {
1321     if (date < *end_of_duration) {
1322       DEBUG("times.interval",
1323             "true: date [" << date << "] < end_of_duration ["
1324             << *end_of_duration << "]");
1325       return true;
1326     }
1327   } else {
1328     DEBUG("times.interval", "false: there is no end_of_duration");
1329     return false;
1330   }
1331 
1332   // If we've reached here, it means the date does not fall into the current
1333   // interval, so we must seek another interval that does match -- unless we
1334   // pass by date in so doing, which means we shouldn't alter the current
1335   // period of the interval at all.
1336 
1337   date_t scan        = *start;
1338   date_t end_of_scan = *end_of_duration;
1339 
1340   DEBUG("times.interval", "date        = " << date);
1341   DEBUG("times.interval", "scan        = " << scan);
1342   DEBUG("times.interval", "end_of_scan = " << end_of_scan);
1343 #if DEBUG_ON
1344   if (finish)
1345     DEBUG("times.interval", "finish      = " << *finish);
1346   else
1347     DEBUG("times.interval", "finish is not set");
1348 #endif
1349 
1350   while (date >= scan && (! finish || scan < *finish)) {
1351     if (date < end_of_scan) {
1352       start           = scan;
1353       end_of_duration = end_of_scan;
1354       next            = none;
1355 
1356       DEBUG("times.interval", "true: start           = " << *start);
1357       DEBUG("times.interval", "true: end_of_duration = " << *end_of_duration);
1358 
1359       resolve_end();
1360 
1361       return true;
1362     }
1363     else if (! allow_shift) {
1364       break;
1365     }
1366 
1367     scan        = duration->add(scan);
1368     end_of_scan = duration->add(scan);
1369 
1370     DEBUG("times.interval", "scan        = " << scan);
1371     DEBUG("times.interval", "end_of_scan = " << end_of_scan);
1372   }
1373 
1374   DEBUG("times.interval", "false: failed scan");
1375 
1376   return false;
1377 }
1378 
operator ++()1379 date_interval_t& date_interval_t::operator++()
1380 {
1381   if (! start)
1382     throw_(date_error, _("Cannot increment an unstarted date interval"));
1383 
1384   stabilize();
1385 
1386   if (! duration)
1387     throw_(date_error,
1388            _("Cannot increment a date interval without a duration"));
1389 
1390   assert(next);
1391 
1392   if (finish && *next >= *finish) {
1393     start = none;
1394   } else {
1395     start = *next;
1396     end_of_duration = duration->add(*start);
1397   }
1398   next = none;
1399 
1400   resolve_end();
1401 
1402   return *this;
1403 }
1404 
dump(std::ostream & out)1405 void date_interval_t::dump(std::ostream& out)
1406 {
1407   out << _("--- Before stabilization ---") << std::endl;
1408 
1409   if (range)
1410     out << _("   range: ") << range->to_string() << std::endl;
1411   if (start)
1412     out << _("   start: ") << format_date(*start) << std::endl;
1413   if (finish)
1414     out << _("  finish: ") << format_date(*finish) << std::endl;
1415 
1416   if (duration)
1417     out << _("duration: ") << duration->to_string() << std::endl;
1418 
1419   optional<date_t> when(begin());
1420   if (! when)
1421     when = CURRENT_DATE();
1422 
1423   stabilize(when);
1424 
1425   out << std::endl
1426       << _("--- After stabilization ---") << std::endl;
1427 
1428   if (range)
1429     out << _("   range: ") << range->to_string() << std::endl;
1430   if (start)
1431     out << _("   start: ") << format_date(*start) << std::endl;
1432   if (finish)
1433     out << _("  finish: ") << format_date(*finish) << std::endl;
1434 
1435   if (duration)
1436     out << _("duration: ") << duration->to_string() << std::endl;
1437 
1438   out << std::endl
1439       << _("--- Sample dates in range (max. 20) ---") << std::endl;
1440 
1441   date_t last_date;
1442 
1443   for (int i = 0; i < 20 && *this; ++i, ++*this) {
1444     out << std::right;
1445     out.width(2);
1446 
1447     if (! last_date.is_not_a_date() && last_date == *start)
1448       break;
1449 
1450     out << (i + 1) << ": " << format_date(*start);
1451     if (duration)
1452       out << " -- " << format_date(*inclusive_end());
1453     out << std::endl;
1454 
1455     if (! duration)
1456       break;
1457 
1458     last_date = *start;
1459   }
1460 }
1461 
next_token()1462 date_parser_t::lexer_t::token_t date_parser_t::lexer_t::next_token()
1463 {
1464   if (token_cache.kind != token_t::UNKNOWN) {
1465     token_t tok = token_cache;
1466     token_cache = token_t();
1467     return tok;
1468   }
1469 
1470   while (begin != end && std::isspace(*begin))
1471     begin++;
1472 
1473   if (begin == end)
1474     return token_t(token_t::END_REACHED);
1475 
1476   switch (*begin) {
1477   case '/': ++begin; return token_t(token_t::TOK_SLASH);
1478   case '-': ++begin; return token_t(token_t::TOK_DASH);
1479   case '.': ++begin; return token_t(token_t::TOK_DOT);
1480   default: break;
1481   }
1482 
1483   string::const_iterator start = begin;
1484 
1485   // If the first character is a digit, try parsing the whole argument as a
1486   // date using the typical date formats.  This allows not only dates like
1487   // "2009/08/01", but also dates that fit the user's --input-date-format,
1488   // assuming their format fits in one argument and begins with a digit.
1489   if (std::isdigit(*begin)) {
1490     string::const_iterator i = begin;
1491     for (i = begin; i != end && ! std::isspace(*i); i++) {}
1492     assert(i != begin);
1493 
1494     string possible_date(start, i);
1495 
1496     try {
1497       date_traits_t traits;
1498       date_t when = parse_date_mask(possible_date.c_str(), &traits);
1499       if (! when.is_not_a_date()) {
1500         begin = i;
1501         return token_t(token_t::TOK_DATE,
1502                        token_t::content_t(date_specifier_t(when, traits)));
1503       }
1504     }
1505     catch (date_error&) {
1506       if (contains(possible_date, "/") ||
1507           contains(possible_date, "-") ||
1508           contains(possible_date, "."))
1509         throw;
1510     }
1511   }
1512 
1513   start = begin;
1514 
1515   string term;
1516   bool alnum = std::isalnum(*begin);
1517   for (; (begin != end && ! std::isspace(*begin) &&
1518           ((alnum && static_cast<bool>(std::isalnum(*begin))) ||
1519            (! alnum && ! static_cast<bool>(std::isalnum(*begin))))); begin++)
1520     term.push_back(*begin);
1521 
1522   if (! term.empty()) {
1523     if (std::isdigit(term[0])) {
1524       return token_t(token_t::TOK_INT,
1525                      token_t::content_t(lexical_cast<unsigned short>(term)));
1526     }
1527     else if (std::isalpha(term[0])) {
1528       to_lower(term);
1529 
1530       if (optional<date_time::months_of_year> month =
1531           string_to_month_of_year(term)) {
1532         return token_t(token_t::TOK_A_MONTH, token_t::content_t(*month));
1533       }
1534       else if (optional<date_time::weekdays> wday =
1535                string_to_day_of_week(term)) {
1536         return token_t(token_t::TOK_A_WDAY, token_t::content_t(*wday));
1537       }
1538       else if (term == _("ago"))
1539         return token_t(token_t::TOK_AGO);
1540       else if (term == _("hence"))
1541         return token_t(token_t::TOK_HENCE);
1542       else if (term == _("from") || term == _("since"))
1543         return token_t(token_t::TOK_SINCE);
1544       else if (term == _("to") || term == _("until"))
1545         return token_t(token_t::TOK_UNTIL);
1546       else if (term == _("in"))
1547         return token_t(token_t::TOK_IN);
1548       else if (term == _("this"))
1549         return token_t(token_t::TOK_THIS);
1550       else if (term == _("next"))
1551         return token_t(token_t::TOK_NEXT);
1552       else if (term == _("last"))
1553         return token_t(token_t::TOK_LAST);
1554       else if (term == _("every"))
1555         return token_t(token_t::TOK_EVERY);
1556       else if (term == _("today"))
1557         return token_t(token_t::TOK_TODAY);
1558       else if (term == _("tomorrow"))
1559         return token_t(token_t::TOK_TOMORROW);
1560       else if (term == _("yesterday"))
1561         return token_t(token_t::TOK_YESTERDAY);
1562       else if (term == _("year"))
1563         return token_t(token_t::TOK_YEAR);
1564       else if (term == _("quarter"))
1565         return token_t(token_t::TOK_QUARTER);
1566       else if (term == _("month"))
1567         return token_t(token_t::TOK_MONTH);
1568       else if (term == _("week"))
1569         return token_t(token_t::TOK_WEEK);
1570       else if (term == _("day"))
1571         return token_t(token_t::TOK_DAY);
1572       else if (term == _("yearly"))
1573         return token_t(token_t::TOK_YEARLY);
1574       else if (term == _("quarterly"))
1575         return token_t(token_t::TOK_QUARTERLY);
1576       else if (term == _("bimonthly"))
1577         return token_t(token_t::TOK_BIMONTHLY);
1578       else if (term == _("monthly"))
1579         return token_t(token_t::TOK_MONTHLY);
1580       else if (term == _("biweekly"))
1581         return token_t(token_t::TOK_BIWEEKLY);
1582       else if (term == _("weekly"))
1583         return token_t(token_t::TOK_WEEKLY);
1584       else if (term == _("daily"))
1585         return token_t(token_t::TOK_DAILY);
1586       else if (term == _("years"))
1587         return token_t(token_t::TOK_YEARS);
1588       else if (term == _("quarters"))
1589         return token_t(token_t::TOK_QUARTERS);
1590       else if (term == _("months"))
1591         return token_t(token_t::TOK_MONTHS);
1592       else if (term == _("weeks"))
1593         return token_t(token_t::TOK_WEEKS);
1594       else if (term == _("days"))
1595         return token_t(token_t::TOK_DAYS);
1596     }
1597     else {
1598       token_t::expected('\0', term[0]);
1599       begin = ++start;
1600     }
1601   } else {
1602     token_t::expected('\0', *begin);
1603   }
1604 
1605   return token_t(token_t::UNKNOWN, token_t::content_t(term));
1606 }
1607 
unexpected()1608 void date_parser_t::lexer_t::token_t::unexpected()
1609 {
1610   switch (kind) {
1611   case END_REACHED:
1612     kind = UNKNOWN;
1613     throw_(date_error, _("Unexpected end of expression"));
1614   default: {
1615     string desc = to_string();
1616     kind = UNKNOWN;
1617     throw_(date_error, _f("Unexpected date period token '%1%'") % desc);
1618   }
1619   }
1620 }
1621 
expected(char wanted,char c)1622 void date_parser_t::lexer_t::token_t::expected(char wanted, char c)
1623 {
1624   if (c == '\0' || c == -1) {
1625     if (wanted == '\0' || wanted == -1)
1626       throw_(date_error, _("Unexpected end"));
1627     else
1628       throw_(date_error, _f("Missing '%1%'") % wanted);
1629   } else {
1630     if (wanted == '\0' || wanted == -1)
1631       throw_(date_error, _f("Invalid char '%1%'") % c);
1632     else
1633       throw_(date_error, _f("Invalid char '%1%' (wanted '%2%')") % c % wanted);
1634   }
1635 }
1636 
1637 namespace {
1638   typedef std::map<std::string, datetime_io_t *> datetime_io_map;
1639   typedef std::map<std::string, date_io_t *>     date_io_map;
1640 
1641   datetime_io_map temp_datetime_io;
1642   date_io_map     temp_date_io;
1643 }
1644 
format_datetime(const datetime_t & when,const format_type_t format_type,const optional<const char * > & format)1645 std::string format_datetime(const datetime_t&             when,
1646                             const format_type_t           format_type,
1647                             const optional<const char *>& format)
1648 {
1649   if (format_type == FMT_WRITTEN) {
1650     return written_datetime_io->format(when);
1651   }
1652   else if (format_type == FMT_CUSTOM && format) {
1653     datetime_io_map::iterator i = temp_datetime_io.find(*format);
1654     if (i != temp_datetime_io.end()) {
1655       return (*i).second->format(when);
1656     } else {
1657       datetime_io_t * formatter = new datetime_io_t(*format, false);
1658       temp_datetime_io.insert(datetime_io_map::value_type(*format, formatter));
1659       return formatter->format(when);
1660     }
1661   }
1662   else if (format_type == FMT_PRINTED) {
1663     return printed_datetime_io->format(when);
1664   }
1665   else {
1666     assert(false);
1667     return empty_string;
1668   }
1669 }
1670 
format_date(const date_t & when,const format_type_t format_type,const optional<const char * > & format)1671 std::string format_date(const date_t&                 when,
1672                         const format_type_t           format_type,
1673                         const optional<const char *>& format)
1674 {
1675   if (format_type == FMT_WRITTEN) {
1676     return written_date_io->format(when);
1677   }
1678   else if (format_type == FMT_CUSTOM && format) {
1679     date_io_map::iterator i = temp_date_io.find(*format);
1680     if (i != temp_date_io.end()) {
1681       return (*i).second->format(when);
1682     } else {
1683       date_io_t * formatter = new date_io_t(*format, false);
1684       temp_date_io.insert(date_io_map::value_type(*format, formatter));
1685       return formatter->format(when);
1686     }
1687   }
1688   else if (format_type == FMT_PRINTED) {
1689     return printed_date_io->format(when);
1690   }
1691   else {
1692     assert(false);
1693     return empty_string;
1694   }
1695 }
1696 
1697 namespace {
1698   bool is_initialized = false;
1699 }
1700 
set_datetime_format(const char * format)1701 void set_datetime_format(const char * format)
1702 {
1703   written_datetime_io->set_format(format);
1704   printed_datetime_io->set_format(format);
1705 }
1706 
set_date_format(const char * format)1707 void set_date_format(const char * format)
1708 {
1709   written_date_io->set_format(format);
1710   printed_date_io->set_format(format);
1711 }
1712 
set_input_date_format(const char * format)1713 void set_input_date_format(const char * format)
1714 {
1715   readers.push_front(shared_ptr<date_io_t>(new date_io_t(format, true)));
1716   convert_separators_to_slashes = false;
1717 }
1718 
times_initialize()1719 void times_initialize()
1720 {
1721   if (! is_initialized) {
1722     input_datetime_io.reset(new datetime_io_t("%Y/%m/%d %H:%M:%S", true));
1723     timelog_datetime_io.reset(new datetime_io_t("%m/%d/%Y %H:%M:%S", true));
1724 
1725     written_datetime_io.reset(new datetime_io_t("%Y/%m/%d %H:%M:%S", false));
1726     written_date_io.reset(new date_io_t("%Y/%m/%d", false));
1727 
1728     printed_datetime_io.reset(new datetime_io_t("%y-%b-%d %H:%M:%S", false));
1729     printed_date_io.reset(new date_io_t("%y-%b-%d", false));
1730 
1731     readers.push_back(shared_ptr<date_io_t>(new date_io_t("%m/%d", true)));
1732     readers.push_back(shared_ptr<date_io_t>(new date_io_t("%Y/%m/%d", true)));
1733     readers.push_back(shared_ptr<date_io_t>(new date_io_t("%Y/%m", true)));
1734     readers.push_back(shared_ptr<date_io_t>(new date_io_t("%y/%m/%d", true)));
1735     readers.push_back(shared_ptr<date_io_t>(new date_io_t("%Y-%m-%d", true)));
1736 
1737     is_initialized = true;
1738   }
1739 }
1740 
times_shutdown()1741 void times_shutdown()
1742 {
1743   if (is_initialized) {
1744     input_datetime_io.reset();
1745     timelog_datetime_io.reset();
1746     written_datetime_io.reset();
1747     written_date_io.reset();
1748     printed_datetime_io.reset();
1749     printed_date_io.reset();
1750 
1751     readers.clear();
1752 
1753     foreach (datetime_io_map::value_type& pair, temp_datetime_io)
1754       checked_delete(pair.second);
1755     temp_datetime_io.clear();
1756 
1757     foreach (date_io_map::value_type& pair, temp_date_io)
1758       checked_delete(pair.second);
1759     temp_date_io.clear();
1760 
1761     is_initialized = false;
1762   }
1763 }
1764 
show_period_tokens(std::ostream & out,const string & arg)1765 void show_period_tokens(std::ostream& out, const string& arg)
1766 {
1767   date_parser_t::lexer_t lexer(arg.begin(), arg.end());
1768 
1769   out << _("--- Period expression tokens ---") << std::endl;
1770 
1771   date_parser_t::lexer_t::token_t token;
1772   do {
1773     token = lexer.next_token();
1774     token.dump(out);
1775     out << ": " << token.to_string() << std::endl;
1776   }
1777   while (token.kind != date_parser_t::lexer_t::token_t::END_REACHED);
1778 }
1779 
1780 } // namespace ledger
1781