Loading...
Searching...
No Matches
times.h
Go to the documentation of this file.
1/*
2 * Copyright (c) 2003-2025, 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
35
44#pragma once
45
46#include "utils.h"
47
48namespace ledger {
49
50DECLARE_EXCEPTION(datetime_error, std::runtime_error);
51DECLARE_EXCEPTION(date_error, std::runtime_error);
52
53typedef boost::posix_time::ptime datetime_t;
54typedef datetime_t::time_duration_type time_duration_t;
55
56inline bool is_valid(const datetime_t& moment) {
57 return ! moment.is_not_a_date_time();
58}
59
60typedef boost::gregorian::date date_t;
61typedef boost::gregorian::date_iterator date_iterator_t;
62
63inline bool is_valid(const date_t& moment) {
64 return ! moment.is_not_a_date();
65}
66
67extern optional<datetime_t> epoch;
68extern optional<int> year_directive_year;
69
70#ifdef BOOST_DATE_TIME_HAS_HIGH_PRECISION_CLOCK
71#define TRUE_CURRENT_TIME() (boost::posix_time::microsec_clock::local_time())
72#define CURRENT_TIME() (epoch ? *epoch : TRUE_CURRENT_TIME())
73#else
74#define TRUE_CURRENT_TIME() (boost::posix_time::second_clock::local_time())
75#define CURRENT_TIME() (epoch ? *epoch : TRUE_CURRENT_TIME())
76#endif
77#define CURRENT_DATE() \
78 (epoch ? epoch->date() : boost::gregorian::day_clock::local_day())
79
80extern date_time::weekdays start_of_week;
81
82optional<date_time::weekdays>
83string_to_day_of_week(const std::string& str);
84optional<date_time::months_of_year>
85string_to_month_of_year(const std::string& str);
86
87datetime_t parse_datetime(const char * str);
88
89inline datetime_t parse_datetime(const std::string& str) {
90 return parse_datetime(str.c_str());
91}
92
93date_t parse_date(const char * str);
94
95inline date_t parse_date(const std::string& str) {
96 return parse_date(str.c_str());
97}
98
102
103std::string format_datetime(const datetime_t& when,
104 const format_type_t format_type = FMT_PRINTED,
105 const optional<const char *>& format = none);
106void set_datetime_format(const char * format);
107
108std::string format_date(const date_t& when,
109 const format_type_t format_type = FMT_PRINTED,
110 const optional<const char *>& format = none);
111void set_date_format(const char * format);
112void set_input_date_format(const char * format);
113
114inline void put_datetime(property_tree::ptree& pt, const datetime_t& when) {
115 pt.put_value(format_datetime(when, FMT_WRITTEN));
116}
117
118inline void put_date(property_tree::ptree& pt, const date_t& when) {
119 pt.put_value(format_date(when, FMT_WRITTEN));
120}
121
123{
127
128 date_traits_t(bool _has_year = false,
129 bool _has_month = false,
130 bool _has_day = false)
131 : has_year(_has_year), has_month(_has_month), has_day(_has_day) {
132 TRACE_CTOR(date_traits_t, "bool, bool, bool");
133 }
135 : has_year(traits.has_year),
136 has_month(traits.has_month),
137 has_day(traits.has_day) {
138 TRACE_CTOR(date_traits_t, "copy");
139 }
140 ~date_traits_t() throw() {
142 }
143
145 has_year = traits.has_year;
146 has_month = traits.has_month;
147 has_day = traits.has_day;
148 return *this;
149 }
150
151 bool operator==(const date_traits_t& traits) const {
152 return (has_year == traits.has_year &&
153 has_month == traits.has_month &&
154 has_day == traits.has_day);
155 }
156};
157
159{
164
168 date_duration_t(skip_quantum_t _quantum, int _length)
169 : quantum(_quantum), length(_length) {
170 TRACE_CTOR(date_duration_t, "skip_quantum_t, int");
171 }
173 : quantum(dur.quantum), length(dur.length) {
175 }
179
180 date_t add(const date_t& date) const {
181 switch (quantum) {
182 case DAYS:
183 return date + gregorian::days(length);
184 case WEEKS:
185 return date + gregorian::weeks(length);
186 case MONTHS:
187 return date + gregorian::months(length);
188 case QUARTERS:
189 return date + gregorian::months(length * 3);
190 case YEARS:
191 return date + gregorian::years(length);
192 }
193#if !defined(__clang__)
194 return date_t();
195#endif
196 }
197
198 date_t subtract(const date_t& date) const {
199 switch (quantum) {
200 case DAYS:
201 return date - gregorian::days(length);
202 case WEEKS:
203 return date - gregorian::weeks(length);
204 case MONTHS:
205 return date - gregorian::months(length);
206 case QUARTERS:
207 return date - gregorian::months(length * 3);
208 case YEARS:
209 return date - gregorian::years(length);
210 }
211#if !defined(__clang__)
212 return date_t();
213#endif
214 }
215
216 string to_string() const {
217 std::ostringstream out;
218
219 out << length << ' ';
220
221 switch (quantum) {
222 case DAYS: out << "day"; break;
223 case WEEKS: out << "week"; break;
224 case MONTHS: out << "month"; break;
225 case QUARTERS: out << "quarter"; break;
226 case YEARS: out << "year"; break;
227 }
228
229 if (length > 1)
230 out << 's';
231
232 return out.str();
233 }
234
236};
237
239{
240 friend class date_parser_t;
241
242public:
243#if 0
244 typedef date_t::year_type year_type;
245#else
246 typedef unsigned short year_type;
247#endif
248 typedef date_t::month_type month_type;
249 typedef date_t::day_type day_type;
250 typedef date_t::day_of_week_type day_of_week_type;
251
252protected:
253 optional<year_type> year;
254 optional<month_type> month;
255 optional<day_type> day;
256 optional<day_of_week_type> wday;
257
258public:
259 date_specifier_t(const optional<year_type>& _year = none,
260 const optional<month_type>& _month = none,
261 const optional<day_type>& _day = none,
262 const optional<day_of_week_type>& _wday = none)
263 : year(_year), month(_month), day(_day), wday(_wday) {
265 "year_type, month_type, day_type, day_of_week_type");
266 }
268 const optional<date_traits_t>& traits = none) {
269 if (! traits || traits->has_year)
270 year = date.year();
271 if (! traits || traits->has_month)
272 month = date.month();
273 if (! traits || traits->has_day)
274 day = date.day();
275
276 TRACE_CTOR(date_specifier_t, "date_t, date_traits_t");
277 }
279 : year(other.year), month(other.month),
280 day(other.day), wday(other.wday) {
282 }
286
287 date_t begin() const;
288 date_t end() const;
289
290 bool is_within(const date_t& date) const {
291 return date >= begin() && date < end();
292 }
293
294 optional<date_duration_t> implied_duration() const {
295 if (day || wday)
297 else if (month)
299 else if (year)
301 else
302 return none;
303 }
304
305 string to_string() const {
306 std::ostringstream out;
307
308 if (year)
309 out << " year " << *year;
310 if (month)
311 out << " month " << *month;
312 if (day)
313 out << " day " << *day;
314 if (wday)
315 out << " wday " << *wday;
316
317 return out.str();
318 }
319};
320
322{
323 friend class date_parser_t;
324
325 optional<date_specifier_t> range_begin;
326 optional<date_specifier_t> range_end;
327
328 bool end_inclusive;
329
330public:
331 date_range_t(const optional<date_specifier_t>& _range_begin = none,
332 const optional<date_specifier_t>& _range_end = none)
333 : range_begin(_range_begin), range_end(_range_end),
334 end_inclusive(false) {
335 TRACE_CTOR(date_range_t, "date_specifier_t, date_specifier_t");
336 }
338 : range_begin(other.range_begin), range_end(other.range_end),
339 end_inclusive(other.end_inclusive) {
340 TRACE_CTOR(date_range_t, "date_range_t");
341 }
342 ~date_range_t() throw() {
344 }
345
346 optional<date_t> begin() const {
347 if (range_begin)
348 return range_begin->begin();
349 else
350 return none;
351 }
352 optional<date_t> end() const {
353 if (range_end) {
354 if (end_inclusive)
355 return range_end->end();
356 else
357 return range_end->begin();
358 } else {
359 return none;
360 }
361 }
362
363 bool is_within(const date_t& date) const {
364 optional<date_t> b = begin();
365 optional<date_t> e = end();
366 bool after_begin = b ? date >= *b : true;
367 bool before_end = e ? date < *e : true;
368 return after_begin && before_end;
369 }
370
371 string to_string() const {
372 std::ostringstream out;
373
374 if (range_begin)
375 out << "from" << range_begin->to_string();
376 if (range_end)
377 out << " to" << range_end->to_string();
378
379 return out.str();
380 }
381};
382
384{
385 typedef variant<int, date_specifier_t, date_range_t> value_type;
386
387 value_type specifier_or_range;
388
389public:
394 : specifier_or_range(other.specifier_or_range) {
396 }
398 : specifier_or_range(specifier) {
399 TRACE_CTOR(date_specifier_or_range_t, "date_specifier_t");
400 }
402 : specifier_or_range(range) {
403 TRACE_CTOR(date_specifier_or_range_t, "date_range_t");
404 }
408
409 optional<date_t> begin() const {
410 if (specifier_or_range.type() == typeid(date_specifier_t))
411 return boost::get<date_specifier_t>(specifier_or_range).begin();
412 else if (specifier_or_range.type() == typeid(date_range_t))
413 return boost::get<date_range_t>(specifier_or_range).begin();
414 else
415 return none;
416 }
417 optional<date_t> end() const {
418 if (specifier_or_range.type() == typeid(date_specifier_t))
419 return boost::get<date_specifier_t>(specifier_or_range).end();
420 else if (specifier_or_range.type() == typeid(date_range_t))
421 return boost::get<date_range_t>(specifier_or_range).end();
422 else
423 return none;
424 }
425
426
427 string to_string() const {
428 std::ostringstream out;
429
430 if (specifier_or_range.type() == typeid(date_specifier_t))
431 out << "in" << boost::get<date_specifier_t>(specifier_or_range).to_string();
432 else if (specifier_or_range.type() == typeid(date_range_t))
433 out << boost::get<date_range_t>(specifier_or_range).to_string();
434
435 return out.str();
436 }
437};
438
439class date_interval_t : public equality_comparable<date_interval_t>
440{
441public:
446
447 optional<date_specifier_or_range_t> range;
448
449 optional<date_t> start; // the real start, after adjustment
450 optional<date_t> finish; // the real end, likewise
452 optional<date_t> next;
453 optional<date_duration_t> duration;
454 optional<date_t> end_of_duration;
455 bool since_specified = false;
456
457 explicit date_interval_t() : aligned(false) {
459 }
460 date_interval_t(const string& str) : aligned(false) {
461 parse(str);
462 TRACE_CTOR(date_interval_t, "const string&");
463 }
465 : range(other.range),
466 start(other.start),
467 finish(other.finish),
468 aligned(other.aligned),
469 next(other.next),
470 duration(other.duration),
474 }
478
479 bool operator==(const date_interval_t& other) const {
480 return (start == other.start &&
481 (! start || *start == *other.start));
482 }
483 bool operator<(const date_interval_t& other) const {
484 return (start == other.start &&
485 (! start || *start < *other.start));
486 }
487
488 operator bool() const {
489 return is_valid();
490 }
491
492 optional<date_t> begin() const {
493 return start ? start : (range ? range->begin() : none);
494 }
495 optional<date_t> end() const {
496 return finish ? finish : (range ? range->end() : none);
497 }
498
499 void parse(const string& str);
500
502 void stabilize(const optional<date_t>& date = none, bool align_intervals = false);
503
504 bool is_valid() const {
505 return static_cast<bool>(start);
506 }
507
512 const bool align_intervals = false,
513 const bool allow_shift = true);
515 return find_period(date, false, false);
516 }
517
518 optional<date_t> inclusive_end() const {
519 if (end_of_duration)
520 return *end_of_duration - gregorian::days(1);
521 else
522 return none;
523 }
524
526
527 void dump(std::ostream& out);
528};
529
532
533void show_period_tokens(std::ostream& out, const string& arg);
534
535std::ostream& operator<<(std::ostream& out, const date_duration_t& duration);
536
537} // namespace ledger
#define CURRENT_DATE()
Definition times.h:77
#define DECLARE_EXCEPTION(name, kind)
Definition error.h:88
General utility facilities used by Ledger.
#define TRACE_DTOR(cls)
Definition utils.h:144
#define TRACE_CTOR(cls, args)
Definition utils.h:143
optional< date_time::months_of_year > string_to_month_of_year(const std::string &str)
void put_date(property_tree::ptree &pt, const date_t &when)
Definition times.h:118
std::string format_datetime(const datetime_t &when, const format_type_t format_type=FMT_PRINTED, const optional< const char * > &format=none)
void set_date_format(const char *format)
optional< datetime_t > epoch
gregorian::date date
Definition utils.h:64
datetime_t::time_duration_type time_duration_t
Definition times.h:54
bool is_valid(const datetime_t &moment)
Definition times.h:56
void set_input_date_format(const char *format)
optional< int > year_directive_year
boost::gregorian::date date_t
Definition times.h:60
boost::gregorian::date_iterator date_iterator_t
Definition times.h:61
void put_datetime(property_tree::ptree &pt, const datetime_t &when)
Definition times.h:114
std::string format_date(const date_t &when, const format_type_t format_type=FMT_PRINTED, const optional< const char * > &format=none)
date_time::weekdays start_of_week
std::ostream & operator<<(std::ostream &out, const account_t &account)
void show_period_tokens(std::ostream &out, const string &arg)
optional< date_time::weekdays > string_to_day_of_week(const std::string &str)
void times_shutdown()
void set_datetime_format(const char *format)
datetime_t parse_datetime(const char *str)
format_type_t
Definition times.h:99
@ FMT_CUSTOM
Definition times.h:100
@ FMT_WRITTEN
Definition times.h:100
@ FMT_PRINTED
Definition times.h:100
boost::posix_time::ptime datetime_t
Definition times.h:53
date_t parse_date(const char *str)
void times_initialize()
datetime_error(const string &why)
Definition times.h:50
date_error(const string &why)
Definition times.h:51
bool operator==(const date_traits_t &traits) const
Definition times.h:151
date_traits_t(const date_traits_t &traits)
Definition times.h:134
date_traits_t & operator=(const date_traits_t &traits)
Definition times.h:144
date_traits_t(bool _has_year=false, bool _has_month=false, bool _has_day=false)
Definition times.h:128
date_t add(const date_t &date) const
Definition times.h:180
static date_t find_nearest(const date_t &date, skip_quantum_t skip)
enum ledger::date_duration_t::skip_quantum_t quantum
date_t subtract(const date_t &date) const
Definition times.h:198
date_duration_t(const date_duration_t &dur)
Definition times.h:172
date_duration_t(skip_quantum_t _quantum, int _length)
Definition times.h:168
string to_string() const
Definition times.h:216
date_specifier_t(const optional< year_type > &_year=none, const optional< month_type > &_month=none, const optional< day_type > &_day=none, const optional< day_of_week_type > &_wday=none)
Definition times.h:259
optional< month_type > month
Definition times.h:254
string to_string() const
Definition times.h:305
optional< day_of_week_type > wday
Definition times.h:256
bool is_within(const date_t &date) const
Definition times.h:290
optional< date_duration_t > implied_duration() const
Definition times.h:294
optional< year_type > year
Definition times.h:253
date_t::day_type day_type
Definition times.h:249
date_t::month_type month_type
Definition times.h:248
date_t::day_of_week_type day_of_week_type
Definition times.h:250
unsigned short year_type
Definition times.h:246
optional< day_type > day
Definition times.h:255
friend class date_parser_t
Definition times.h:240
date_specifier_t(const date_specifier_t &other)
Definition times.h:278
date_specifier_t(const date_t &date, const optional< date_traits_t > &traits=none)
Definition times.h:267
date_range_t(const optional< date_specifier_t > &_range_begin=none, const optional< date_specifier_t > &_range_end=none)
Definition times.h:331
optional< date_t > end() const
Definition times.h:352
optional< date_t > begin() const
Definition times.h:346
bool is_within(const date_t &date) const
Definition times.h:363
date_range_t(const date_range_t &other)
Definition times.h:337
friend class date_parser_t
Definition times.h:323
string to_string() const
Definition times.h:371
optional< date_t > end() const
Definition times.h:417
date_specifier_or_range_t(const date_range_t &range)
Definition times.h:401
optional< date_t > begin() const
Definition times.h:409
date_specifier_or_range_t(const date_specifier_t &specifier)
Definition times.h:397
date_specifier_or_range_t(const date_specifier_or_range_t &other)
Definition times.h:393
optional< date_t > begin() const
Definition times.h:492
void dump(std::ostream &out)
optional< date_t > start
Definition times.h:449
optional< date_t > finish
Definition times.h:450
bool is_valid() const
Definition times.h:504
void stabilize(const optional< date_t > &date=none, bool align_intervals=false)
static date_t subtract_duration(const date_t &date, const date_duration_t &duration)
date_interval_t & operator++()
bool operator<(const date_interval_t &other) const
Definition times.h:483
optional< date_t > end() const
Definition times.h:495
optional< date_t > end_of_duration
Definition times.h:454
optional< date_t > next
Definition times.h:452
date_interval_t(const string &str)
Definition times.h:460
void parse(const string &str)
static date_t add_duration(const date_t &date, const date_duration_t &duration)
bool operator==(const date_interval_t &other) const
Definition times.h:479
date_interval_t(const date_interval_t &other)
Definition times.h:464
optional< date_t > inclusive_end() const
Definition times.h:518
bool within_period(const date_t &date=(epoch ? epoch->date() :boost::gregorian::day_clock::local_day()))
Definition times.h:514
optional< date_specifier_or_range_t > range
Definition times.h:447
bool find_period(const date_t &date=(epoch ? epoch->date() :boost::gregorian::day_clock::local_day()), const bool align_intervals=false, const bool allow_shift=true)
Find the current or next period containing date.
optional< date_duration_t > duration
Definition times.h:453