allow creating Time objects from formatted timestamps
[libi2ncommon] / src / timefunc.hxx
CommitLineData
0e23f538
TJ
1/*
2The software in this package is distributed under the GNU General
3Public License version 2 (with a special exception described below).
4
5A copy of GNU General Public License (GPL) is included in this distribution,
6in the file COPYING.GPL.
7
8As a special exception, if other files instantiate templates or use macros
9or inline functions from this file, or you compile this file and link it
10with other works to produce a work based on this file, this file
11does not by itself cause the resulting work to be covered
12by the GNU General Public License.
13
14However the source code for this file must still be made available
15in accordance with section (3) of the GNU General Public License.
16
17This exception does not invalidate any other reasons why a work based
18on this file might be covered by the GNU General Public License.
19*/
1b5dfd98
TJ
20/** @file
21 * @brief time related functions.
22 *
d70f7269 23 * @copyright Copyright © 2001-2018 by Intra2net AG
1b5dfd98 24 */
e93545dd
GE
25
26#ifndef __TIMEFUNC_HXX
27#define __TIMEFUNC_HXX
28
e36ca33c 29#include <climits>
8b5814e2 30#include <errno.h>
1b5dfd98 31#include <list>
e36ca33c 32#include <string>
1b5dfd98 33
8b5814e2
PG
34#include <boost/optional.hpp>
35
0c7e72d7 36#include <week.hpp>
f1499910 37
e36ca33c
PG
38#include "stringfunc.hxx"
39
40#if __cplusplus >= 201103
41# define CONSTEXPR constexpr
42# define NOEXCEPT noexcept
43#else
44# define CONSTEXPR
45# define NOEXCEPT
46#endif
47
48#define TIME_CONST_FACTOR_NANO (1000L * 1000 * 1000)
49
e93545dd
GE
50double prec_time(void);
51
dad9e26f 52time_t date_to_seconds(const std::string &date);
e93545dd
GE
53
54std::string make_nice_time(int seconds);
7839bd53 55std::string format_full_time(time_t seconds);
25f3d405 56std::string format_date(time_t seconds);
d70f7269
PG
57std::string format_iso8601(const struct tm &tm, const bool date=true,
58 const bool time=true, const bool tz=true);
59std::string format_iso8601(time_t t, const bool utc=true,
60 const bool date=true, const bool time=true,
61 const bool tz=true);
62inline std::string format_iso8601(const struct timespec ts, const bool utc=true,
63 const bool date=true, const bool time=true,
64 const bool tz=true)
65{ return format_iso8601 (ts.tv_sec, utc, date, time, tz); }
66
2795e39c
PG
67boost::optional<struct tm> scan_iso8601 (const char *s,
68 const bool date=true, const bool time=true,
69 const bool tz=true) NOEXCEPT;
70inline boost::optional<struct tm> scan_iso8601 (const std::string &s,
71 const bool date=true,
72 const bool time=true,
73 const bool tz=true) NOEXCEPT
74{ return scan_iso8601 (s.c_str (), date, time, tz); }
75
76
87869870 77void seconds_to_hour_minute(int seconds, int *hour, int *minute);
c0368918
GE
78void split_daysec(int daysec, int *outhours=NULL, int *outminutes=NULL, int *outseconds=NULL);
79std::string output_hour_minute(int hour, int minute, bool h_for_00=true, int seconds=0);
2c66f490
GE
80
81inline std::string output_hour_minute_from_seconds(int seconds)
82{
83 int hour, minute;
c0368918 84 split_daysec(seconds,&hour,&minute);
2c66f490
GE
85 return output_hour_minute(hour,minute);
86}
e93545dd 87
4e157d1d
TJ
88std::string get_month_name(unsigned char month);
89
1b5dfd98
TJ
90/**
91 * @brief structure representing a single (half-open) interval.
92 */
93class Interval
94{
95 public:
96 Interval()
97 : m_lower_bound(0)
98 , m_upper_bound(0)
80f30818
TJ
99 , m_weak_mark(0)
100 , m_changed(false)
1b5dfd98
TJ
101 {
102 } //
103
80f30818 104 Interval( unsigned int start, unsigned int end, int weak_mark= 0 )
1b5dfd98
TJ
105 : m_lower_bound(start)
106 , m_upper_bound(end)
80f30818
TJ
107 , m_weak_mark(weak_mark)
108 , m_changed(false)
1b5dfd98
TJ
109 {
110 } //
111
d181c3bc
TJ
112
113 void clear();
114
1b5dfd98
TJ
115 bool is_valid() const
116 {
117 return m_lower_bound <= m_upper_bound;
118 } // eo is_valid() const
119
120 bool empty() const
121 {
122 return m_lower_bound == m_upper_bound;
123 } // eo empty() const
124
125
126 unsigned int lower_bound() const { return m_lower_bound; }
127 unsigned int upper_bound() const { return m_upper_bound; }
128
80f30818
TJ
129 int weak_mark() const { return m_weak_mark; }
130
131 bool changed() const { return m_changed; }
132
1b5dfd98
TJ
133
134 bool operator== (const Interval& other) const
135 {
136 return m_lower_bound == other.m_lower_bound and m_upper_bound == other.m_upper_bound;
137 } // eo operator==(const Interval&)
138
139
ebc3b584
TJ
140 bool operator!=(const Interval& other) const
141 {
142 return not (*this == other);
143 } // eo operator!=(const Interval&)
144
145
1b5dfd98
TJ
146 /**
147 * @brief less operator. compares only the start times!
148 * @param other the other interval to compare with.
149 * @return @a true if the current start is less than the other start.
150 */
151 bool operator<(const Interval& other) const
152 {
153 return m_lower_bound < other.m_lower_bound;
154 } // eo operator<(const Interval&)
155
156
157 bool intersects(const Interval& other) const;
158 bool contains(const Interval& other) const;
159
160
161 protected:
162
163 friend class Intervals;
164
165 unsigned int m_lower_bound;
166 unsigned int m_upper_bound;
167
80f30818
TJ
168 int m_weak_mark;
169 bool m_changed;
170
1b5dfd98
TJ
171}; // eo Interval
172
173
174
175/**
176 * @brief structure representing a combination of single disjoint (half open) intervals.
177 *
178 * Basic idea is that this structure provides an interface for adding and
179 * subtracting single intervals and keeps the internal list of intervals as
180 * a list of disjoint intervals.
181 *
182 * @note the list is sorted by the start; lower comes first.
183 *
184 * @note the class provides some methods similar to STL container classes;
185 * i.e. it can be used with (at least some) STL algorithms.
186 *
187 * @internal
188 * we use a std::list for the intervals; this means we don't invalidate all
189 * iterators when we insert or delete within loops...
190 * And we use that fact!!
191 */
192class Intervals
193{
194 public:
195 typedef std::list< Interval > IntervalList;
196 typedef IntervalList::value_type value_type;
197 typedef IntervalList::const_iterator const_iterator;
198 typedef IntervalList::const_iterator iterator; // we allow only const...
199 typedef IntervalList::size_type size_type;
200
201 public:
202 Intervals();
203
204 void add(const Interval& new_frame);
205 void sub(const Interval& new_frame);
206
d181c3bc 207 void clear();
1b5dfd98
TJ
208
209 const_iterator begin() const { return m_intervals.begin(); }
210 const_iterator end() const { return m_intervals.end(); }
211
212 bool empty() const { return m_intervals.empty(); }
213 const Interval& front() const { return m_intervals.front(); }
214 const Interval& back() const { return m_intervals.back(); }
215
216 size_type size() const { return m_intervals.size(); }
217
218 bool intersects(const Interval& other) const;
219 bool intersects(const Intervals& other) const;
220
221 bool contains(const Interval& other) const;
222 bool contains(const Intervals& other) const;
223
e156de7c
TJ
224 bool contains_exact(const Interval& other) const;
225
1b5dfd98 226 bool operator==(const Intervals& other) const;
ebc3b584 227 bool operator!=(const Intervals& other) const { return not (*this == other) ; }
1b5dfd98
TJ
228
229 Intervals& operator+=(const Interval& other);
230 Intervals& operator-=(const Interval& other);
231
232 Intervals& operator+=(const Intervals& other);
233 Intervals& operator-=(const Intervals& other);
234
235 protected:
236
237 std::list< Interval > m_intervals;
238
239}; // eo Intervals
240
241
96d0be2e
TJ
242/*
243** clock funcs:
244*/
245
246
247bool monotonic_clock_gettime(long int& seconds, long int& nano_seconds);
b7e17426 248long long monotonic_clock_gettime_nano();
96d0be2e
TJ
249
250bool realtime_clock_gettime(long int& seconds, long int& nano_seconds);
251
8b5814e2
PG
252namespace I2n {
253
254namespace clock {
255
e36ca33c
PG
256 namespace {
257
258 /* helper for ctor initializer list; we still lack aggregate initializers */
259 struct timespec
260 timespec_of_parts (const time_t sec, const long nsec)
261 {
262 struct timespec ret = { sec, nsec};
263
264 return ret;
265 }
266
267 } /* [namespace] */
268
8b5814e2
PG
269 namespace type {
270 /*
271 * represent the clock id options from clock_gettime(2) as
272 * a pair of enums. by default, CLOCK_MONOTONIC_COARSE is used
273 * everywhere since that’s what we want in most cases.
274 */
275 enum id {
276 mono, /* CLOCK_MONOTONIC_COARSE */
277 real, /* CLOCK_REALIME_COARSE */
278 boot, /* CLOCK_BOOTTIME */
279 cpu, /* CLOCK_CPUTIME_* */
280 };
281
282 /*
283 * for clocks that support it: non-coarse or raw variants; if a variant
284 * does not apply to a given clock, it is ignored
285 */
286 enum variant {
287 dflt, /* all */
288 exact, /* mono, real: not (*_COARSE) */
289 raw, /* mono: _RAW */
290 process, /* cpu: *_PROCESS_* */
291 thread, /* cpu: *_THREAD_* */
292 };
293
294 } /* [namespace type] */
295
296
297 class Time {
298
299 private:
e36ca33c 300 struct timespec value;
8b5814e2
PG
301
302 public:
e36ca33c
PG
303 enum type::id id;
304 enum type::variant variant;
305 int err;
306
307 private:
308 /*
309 * Handle decimal part (nanosecond) overflow; this is performed
310 * after arithmetic operations and whenever we there is the
311 * possibility of unsanitized input for example in constructors
312 * from other types and suchlike.
313 *
314 * POSIX defines the ns part as *long*. Technically, that means
315 * that on machines where *sizeof long* equals *sizeof int*, it can
316 * represent only up to arund 2.1 seconds. In this range, the loop
317 * version is most likely faster than division. However, since in
318 * practice *long* is 8 bytes just about anywhere, we have to
319 * handle greater dividends first.
320 */
321 inline void
322 carry_nsec (void)
323 {
324# if LONG_BIT > 32
325 if ( this->value.tv_nsec < -3L * TIME_CONST_FACTOR_NANO
326 || this->value.tv_nsec > 3L * TIME_CONST_FACTOR_NANO)
327 {
328 const long sec = this->value.tv_nsec / TIME_CONST_FACTOR_NANO;
329 this->value.tv_nsec -= sec * TIME_CONST_FACTOR_NANO;
330 this->value.tv_sec += sec;
331 }
332# endif /* [LONG_BIT > 32] */
333 while (this->value.tv_nsec >= TIME_CONST_FACTOR_NANO) {
334 this->value.tv_sec += 1;
335 this->value.tv_nsec -= TIME_CONST_FACTOR_NANO;
336 }
337
338 while (this->value.tv_nsec < 0) {
339 this->value.tv_sec -= 1;
340 this->value.tv_nsec += TIME_CONST_FACTOR_NANO;
341 }
342 }
8b5814e2
PG
343
344 /* ctors *************************************************************/
345 public:
346
347 Time (const enum type::id id = type::mono,
e36ca33c
PG
348 const enum type::variant var = type::dflt) NOEXCEPT;
349
350 inline Time (const Time &t) NOEXCEPT
351 : value (t.value)
352 , id (t.id)
353 , variant (t.variant)
354 , err (t.err)
355 { }
356
357 inline Time (const time_t sec,
358 const long nsec = 0,
359 const enum type::id id = type::mono,
360 const enum type::variant var = type::dflt,
361 const int err = 0) NOEXCEPT
362 : value (timespec_of_parts (sec, nsec))
363 , id (id)
364 , variant (var)
365 , err (err)
366 { this->carry_nsec (); }
367
2795e39c
PG
368 Time (const struct tm &tm,
369 const enum type::id id = type::mono,
370 const enum type::variant var = type::dflt) NOEXCEPT;
371
e36ca33c 372 /* value read access *************************************************/
8b5814e2
PG
373 public:
374
e36ca33c 375 inline CONSTEXPR const struct timespec &get_time (void) const NOEXCEPT
8b5814e2
PG
376 { return this->value; }
377
e36ca33c
PG
378 inline CONSTEXPR const time_t &get_sec (void) const NOEXCEPT
379 { return this->value.tv_sec; }
8b5814e2 380
e36ca33c 381 inline CONSTEXPR const long &get_nsec (void) const NOEXCEPT
8b5814e2
PG
382 { return this->value.tv_nsec; }
383
e36ca33c 384 int64_t as_nanosec (void) const NOEXCEPT;
8b5814e2 385
e36ca33c 386 long as_nanosec_L (void) const NOEXCEPT;
8b5814e2 387
e36ca33c 388 /* value write access ************************************************/
8b5814e2
PG
389 public:
390
391 inline void
e36ca33c
PG
392 swap (Time &t) NOEXCEPT
393 {
394 std::swap (this->value , t.value );
395 std::swap (this->id , t.id );
396 std::swap (this->variant, t.variant);
397 std::swap (this->err , t.err );
398 }
399
400 Time &operator= (Time t) NOEXCEPT;
401
402 Time &operator= (struct timespec ts) NOEXCEPT;
8b5814e2
PG
403
404 inline void
e36ca33c
PG
405 set (const struct timespec &ts) NOEXCEPT
406 {
407 this->value = ts;
408 this->carry_nsec ();
409 }
410
411 inline void
412 set (const time_t sec,
413 const long nsec,
414 const enum type::id id = type::mono,
415 const enum type::variant var = type::dflt) NOEXCEPT
8b5814e2
PG
416 {
417 this->value.tv_sec = sec;
418 this->value.tv_nsec = nsec;
e36ca33c
PG
419 this->id = id;
420 this->variant = var;
421
422 this->carry_nsec ();
423 }
8b5814e2 424
e36ca33c
PG
425 bool set (void) NOEXCEPT;
426
427 void unset (void) NOEXCEPT;
428
429 /* arithmetic ********************************************************/
430 public:
8b5814e2 431
e36ca33c
PG
432 Time &add (const time_t sec, const long nsec) NOEXCEPT;
433
434 Time &subtract (const time_t sec, const long nsec) NOEXCEPT;
435
436 inline Time &add (const Time &t2) NOEXCEPT
437 { return this->add (t2.value.tv_sec, t2.value.tv_nsec); }
438
439 inline Time &add (const time_t t2) NOEXCEPT
440 { return this->add (t2, 0L); };
441
442 inline Time &subtract (const Time &t2) NOEXCEPT
443 { return this->subtract (t2.value.tv_sec, t2.value.tv_nsec); }
444
445 inline Time &subtract (const time_t t2) NOEXCEPT
446 { return this->subtract (t2, 0L); };
447
448 Time &scale (const time_t factor) NOEXCEPT;
449
450 friend int compare (const Time &t1, const Time &t2) NOEXCEPT;
451
452 inline Time
453 difference (const Time &t) NOEXCEPT
454 { return (*this < t) ? t - *this : *this - t; }
455
456 /* overloads *********************************************************/
457 public:
458
459 inline Time
460 operator+ (const Time &t2) const NOEXCEPT
461 { return Time (*this).add (t2); }
462
463 inline Time
464 operator+ (const time_t t2) const NOEXCEPT
465 { return Time (*this).add (t2); }
466
467 inline Time &
468 operator+= (const Time &t2) NOEXCEPT
469 { return this->add (t2); }
470
471 inline Time &
472 operator+= (const time_t t2) NOEXCEPT
473 { return this->add (t2); }
474
475 inline Time
476 operator- (const Time &t2) const NOEXCEPT
477 { return Time (*this).subtract (t2); }
478
479 inline Time
480 operator- (const time_t t2) const NOEXCEPT
481 { return Time (*this).subtract (t2); }
482
483 inline Time &
484 operator-= (const Time &t2) NOEXCEPT
485 { return this->subtract (t2); }
486
487 inline Time &
488 operator-= (const time_t t2) NOEXCEPT
489 { return this->subtract (t2); }
490
491 inline Time
492 operator* (const time_t factor) const NOEXCEPT
493 { return Time (*this).scale (factor); }
494
495 inline Time &
496 operator*= (const time_t factor) NOEXCEPT
497 { return this->scale (factor); }
498
499 friend CONSTEXPR bool
500 operator== (const Time &t1, const Time &t2) NOEXCEPT;
501
502 friend CONSTEXPR bool
503 operator< (const Time &t1, const Time &t2) NOEXCEPT;
504
505 friend CONSTEXPR bool
506 operator> (const Time &t1, const Time &t2) NOEXCEPT;
507
508 friend std::ostream &
509 operator<< (std::ostream &os, const Time &t);
8b5814e2 510
72acd54c
PG
511 /* formatting ********************************************************/
512 public:
513
514 boost::optional<std::string>
515 format_iso8601 (const bool utc = true,
516 const bool date = true,
517 const bool time = true,
518 const bool tz = true) const;
519
520 boost::optional<std::string> make_nice_time (void) const;
521 boost::optional<std::string> format_full_time (void) const;
522 boost::optional<std::string> format_date (void) const;
523
8b5814e2
PG
524 }; /* [class Time] */
525
e36ca33c
PG
526 inline Time
527 operator+ (const time_t t1, const Time &t2) NOEXCEPT
528 { return Time (t1) + t2; }
529
530 inline Time
531 operator- (const time_t t1, const Time &t2) NOEXCEPT
532 { return Time (t1) - t2; }
533
534 int compare (const Time &t1, const Time &t2) NOEXCEPT;
535
536 /*
537 * comparison for equality also considers the clock type;
538 */
539 inline CONSTEXPR bool
540 operator== (const Time &t1, const Time &t2) NOEXCEPT
541 {
542 return t1.id == t2.id && t1.variant == t2.variant
543 && t1.value.tv_sec == t2.value.tv_sec
544 && t1.value.tv_nsec == t2.value.tv_nsec
545 ;
546 }
547
548 /* these ignore the *id* and *variant* fields */
549 inline CONSTEXPR bool
550 operator< (const Time &t1, const Time &t2) NOEXCEPT
551 {
552 return t1.value.tv_sec < t2.value.tv_sec
553 || ( t1.value.tv_sec == t2.value.tv_sec
554 && t1.value.tv_nsec < t2.value.tv_nsec)
555 ;
556 }
557
558 /* these ignore the *id* and *variant* fields */
559 inline CONSTEXPR bool
560 operator> (const Time &t1, const Time &t2) NOEXCEPT
561 {
562 return t1.value.tv_sec > t2.value.tv_sec
563 || ( t1.value.tv_sec == t2.value.tv_sec
564 && t1.value.tv_nsec > t2.value.tv_nsec)
565 ;
566 }
567
568 inline std::ostream &
569 operator<< (std::ostream &os, const Time &t)
570 {
571 os << I2n::to_string (t.value.tv_sec) << "ms, "
572 << I2n::to_string (t.value.tv_nsec) << "ns"
573 ;
574
575 return os;
576 }
577
8b5814e2
PG
578 boost::optional<Time>
579 now (const enum type::id id = type::mono,
e36ca33c
PG
580 const enum type::variant var = type::dflt) NOEXCEPT;
581
582 Time
583 zero (const enum type::id id = type::mono,
584 const enum type::variant var = type::dflt) NOEXCEPT;
8b5814e2 585
2795e39c
PG
586 boost::optional<Time>
587 time_of_iso8601 (const std::string &s,
588 const bool date = true,
589 const bool time = true,
590 const bool tz = true,
591 const enum type::id id = type::real,
592 const enum type::variant var = type::dflt) NOEXCEPT;
593
8b5814e2
PG
594} /* [namespace clock] */
595
596} /* [namespace I2n] */
1b5dfd98
TJ
597
598
e93545dd 599#endif