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