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