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