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