allow creating Time objects from formatted timestamps
[libi2ncommon] / src / timefunc.hxx
1 /*
2 The software in this package is distributed under the GNU General
3 Public License version 2 (with a special exception described below).
4
5 A copy of GNU General Public License (GPL) is included in this distribution,
6 in the file COPYING.GPL.
7
8 As a special exception, if other files instantiate templates or use macros
9 or inline functions from this file, or you compile this file and link it
10 with other works to produce a work based on this file, this file
11 does not by itself cause the resulting work to be covered
12 by the GNU General Public License.
13
14 However the source code for this file must still be made available
15 in accordance with section (3) of the GNU General Public License.
16
17 This exception does not invalidate any other reasons why a work based
18 on this file might be covered by the GNU General Public License.
19 */
20 /** @file
21  * @brief time related functions.
22  *
23  * @copyright Copyright © 2001-2018 by Intra2net AG
24  */
25
26 #ifndef __TIMEFUNC_HXX
27 #define __TIMEFUNC_HXX
28
29 #include <climits>
30 #include <errno.h>
31 #include <list>
32 #include <string>
33
34 #include <boost/optional.hpp>
35
36 #include <week.hpp>
37
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
50 double prec_time(void);
51
52 time_t date_to_seconds(const std::string &date);
53
54 std::string make_nice_time(int seconds);
55 std::string format_full_time(time_t seconds);
56 std::string format_date(time_t seconds);
57 std::string format_iso8601(const struct tm &tm, const bool date=true,
58                            const bool time=true, const bool tz=true);
59 std::string format_iso8601(time_t t, const bool utc=true,
60                            const bool date=true, const bool time=true,
61                            const bool tz=true);
62 inline 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
67 boost::optional<struct tm> scan_iso8601 (const char *s,
68                                          const bool date=true, const bool time=true,
69                                          const bool tz=true) NOEXCEPT;
70 inline 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
77 void seconds_to_hour_minute(int seconds, int *hour, int *minute);
78 void split_daysec(int daysec, int *outhours=NULL, int *outminutes=NULL, int *outseconds=NULL);
79 std::string output_hour_minute(int hour, int minute, bool h_for_00=true, int seconds=0);
80
81 inline std::string output_hour_minute_from_seconds(int seconds)
82 {
83     int hour, minute;
84     split_daysec(seconds,&hour,&minute);
85     return output_hour_minute(hour,minute);
86 }
87
88 std::string get_month_name(unsigned char month);
89
90 /**
91  * @brief structure representing a single (half-open) interval.
92  */
93 class Interval
94 {
95     public:
96         Interval()
97         : m_lower_bound(0)
98         , m_upper_bound(0)
99         , m_weak_mark(0)
100         , m_changed(false)
101         {
102         } //
103
104         Interval( unsigned int start, unsigned int end, int weak_mark= 0 )
105         : m_lower_bound(start)
106         , m_upper_bound(end)
107         , m_weak_mark(weak_mark)
108         , m_changed(false)
109         {
110         } //
111
112
113         void clear();
114
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
129         int weak_mark() const { return m_weak_mark; }
130
131         bool changed() const { return m_changed; }
132
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
140         bool  operator!=(const Interval& other) const
141         {
142             return not (*this == other);
143         } // eo operator!=(const Interval&)
144
145
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
168         int m_weak_mark;
169         bool m_changed;
170
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  */
192 class 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
207         void clear();
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
224         bool contains_exact(const Interval& other) const;
225
226         bool operator==(const Intervals& other) const;
227         bool operator!=(const Intervals& other) const { return not (*this == other) ; }
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
242 /*
243 ** clock funcs:
244 */
245
246
247 bool monotonic_clock_gettime(long int& seconds, long int& nano_seconds);
248 long long monotonic_clock_gettime_nano();
249
250 bool realtime_clock_gettime(long int& seconds, long int& nano_seconds);
251
252 namespace I2n {
253
254 namespace clock {
255
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
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:
300             struct timespec     value;
301
302         public:
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             }
343
344         /* ctors *************************************************************/
345         public:
346
347             Time (const enum type::id      id  = type::mono,
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
368             Time (const struct tm          &tm,
369                   const enum type::id       id  = type::mono,
370                   const enum type::variant  var = type::dflt) NOEXCEPT;
371
372         /* value read access *************************************************/
373         public:
374
375             inline CONSTEXPR const struct timespec &get_time (void) const NOEXCEPT
376             { return this->value; }
377
378             inline CONSTEXPR const time_t &get_sec (void) const NOEXCEPT
379             { return this->value.tv_sec; }
380
381             inline CONSTEXPR const long &get_nsec (void) const NOEXCEPT
382             { return this->value.tv_nsec; }
383
384             int64_t as_nanosec (void) const NOEXCEPT;
385
386             long as_nanosec_L (void) const NOEXCEPT;
387
388         /* value write access ************************************************/
389         public:
390
391             inline void
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;
403
404             inline void
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
416             {
417                 this->value.tv_sec  = sec;
418                 this->value.tv_nsec = nsec;
419                 this->id      = id;
420                 this->variant = var;
421
422                 this->carry_nsec ();
423             }
424
425             bool set (void) NOEXCEPT;
426
427             void unset (void) NOEXCEPT;
428
429         /* arithmetic ********************************************************/
430         public:
431
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);
510
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
524     }; /* [class Time] */
525
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
578     boost::optional<Time>
579     now (const enum type::id      id  = type::mono,
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;
585
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
594 } /* [namespace clock] */
595
596 } /* [namespace I2n] */
597
598
599 #endif