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