replace obsolete call to ftime(3)
[libi2ncommon] / src / timefunc.cpp
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
27 #include <cstdio>
28 #include <errno.h>
29 #include <string>
30 #include <sstream>
31 #include <iostream>
32 #include <iomanip>
33 #include <bitset>
34 #include <stdexcept>
35 #include <iterator>
36 #include <algorithm>
37
38 #include <time.h>
39 #include <unistd.h>
40 #include <string.h>
41
42 #include <timefunc.hxx>
43 #include <i18n.h>
44
45
46 // define missing POSIX.1b constants...
47
48 #ifndef CLOCK_REALTIME
49 #define CLOCK_REALTIME 0
50 #endif
51 #ifndef CLOCK_MONOTONIC
52 #define CLOCK_MONOTONIC 1
53 #endif
54
55
56
57 using namespace std;
58
59 double prec_time(void)
60 {
61     struct timespec ts = { 0 };
62     double ret = 0.0;
63
64     if (clock_gettime(CLOCK_REALTIME_COARSE, &ts) == -1) {
65         /* Something’s wrong on the kernel end! */
66         return ret;
67     }
68
69     ret = static_cast<double>(ts.tv_sec);
70     ret += static_cast<double>(ts.tv_nsec)
71          / static_cast<double>(TIME_CONST_FACTOR_NANO);
72
73     return ret;
74 }
75
76 // converts ISO-DATE: 2003-06-13
77 time_t date_to_seconds(const std::string &date)
78 {
79     time_t rtn = 0;
80     int year = -1, month = -1, day = -1;
81     
82     string::size_type pos = date.find("-");
83     if (pos == string::npos)
84         return rtn;
85     
86     istringstream in(string(date,0,pos));
87     in >> year;
88     year -= 1900;
89         
90     string dstr(date, pos+1);
91     if ((pos = dstr.find("-")) == string::npos)
92         return rtn;
93     
94     in.clear();
95     in.str(string(dstr, 0, pos));
96     in >> month;
97     month -= 1;
98         
99     in.clear();
100     in.str(string(dstr, pos+1));
101     in >> day;
102     
103     if (year < 0 || month == -1 || day == -1)
104         return rtn;
105
106     struct tm tm_struct;
107     memset(&tm_struct, 0, sizeof(struct tm));
108     tm_struct.tm_year = year;
109     tm_struct.tm_mon = month;
110     tm_struct.tm_mday = day;
111     tm_struct.tm_isdst = -1;
112     
113     rtn = mktime (&tm_struct);
114     return rtn;
115 }
116
117 string make_nice_time(int seconds)
118 {
119     ostringstream out;
120
121     int days=seconds/86400;
122     seconds%=86400;
123
124     int hours,minutes;
125     split_daysec(seconds,&hours,&minutes,&seconds);
126     
127     if (days>0)
128         out << days << " " << i18n_plural("day", "days", days) << ", ";
129
130     out << setfill('0');
131     out << setw(2) << hours << ':' << setw(2) << minutes << ':' << setw(2) << seconds;
132
133     return out.str();
134 }
135
136 string format_full_time(time_t seconds)
137 {
138     char buf[50];
139     memset (buf, 0, 50);
140     struct tm ta;
141     if (localtime_r((time_t *)&seconds, &ta) == NULL)
142         memset (&ta, 0, sizeof(struct tm));
143
144     strftime (buf, 49, "%d.%m.%Y %H:%M", &ta);
145     return string(buf);
146 }
147
148 string format_date(time_t seconds)
149 {
150     char buf[50];
151     memset (buf, 0, 50);
152     struct tm ta;
153     if (localtime_r((time_t *)&seconds, &ta) == NULL)
154         memset (&ta, 0, sizeof(struct tm));
155
156     strftime (buf, 49, "%d.%m.%Y", &ta);
157     return string(buf);
158 }
159
160 void seconds_to_hour_minute(int seconds, int *hour, int *minute)
161 {
162     if (hour != NULL) {
163         *hour = 0;
164         while (seconds >= 3600) {
165             seconds-=3600;
166             (*hour)++;
167         }
168     }
169
170     if (minute != NULL) {
171         *minute = 0;
172         while (seconds >= 60) {
173             seconds-=60;
174             (*minute)++;
175         }
176     }
177 }
178
179 /**
180     * Split seconds into hours, minutes and seconds
181     * @param [in] daysec Seconds since start of day
182     * @param [out] outhours hours
183     * @param [out] outminutes minutes
184     * @param [out] outseconds seconds
185     */
186 void split_daysec(int daysec, int *outhours, int *outminutes, int *outseconds)
187 {
188     int hours=daysec/3600;
189     daysec%=3600;
190
191     int minutes=daysec/60;
192     daysec%=60;
193
194     if (outhours)
195         *outhours=hours;
196
197     if (outminutes)
198         *outminutes=minutes;
199
200     if (outseconds)
201         *outseconds=daysec;
202 }
203
204 std::string output_hour_minute(int hour, int minute, bool h_for_00, int seconds)
205 {
206     ostringstream out;
207     
208     if (hour >= 0 && hour < 10)
209         out << '0';
210     out << hour;
211     
212     if (!h_for_00 || minute != 0 || seconds > 0)
213     {
214         out << ':';
215         if (minute >= 0 && minute < 10)
216             out << '0';
217         out << minute;
218     }
219     else
220         out << 'h';
221
222     if (seconds > 0)
223     {
224         out << ':';
225         if (seconds > 0 && seconds < 10)
226             out << '0';
227         out << seconds;
228     }
229
230     return out.str();
231 }
232
233 string get_month_name(unsigned char month)
234 {
235     string rtn;
236     switch(month) {
237         case 1:
238             rtn = i18n("January");
239             break;
240         case 2:
241             rtn = i18n("February");
242             break;
243         case 3:
244             rtn = i18n("March");
245             break;
246         case 4:
247             rtn = i18n("April");
248             break;
249         case 5:
250             rtn = i18n("May");
251             break;
252         case 6:
253             rtn = i18n("June");
254             break;
255         case 7:
256             rtn = i18n("July");
257             break;
258         case 8:
259             rtn = i18n("August");
260             break;
261         case 9:
262             rtn = i18n("September");
263             break;
264         case 10:
265             rtn = i18n("October");
266             break;
267         case 11:
268             rtn = i18n("November");
269             break;
270         case 12:
271             rtn = i18n("December");
272             break;
273         default:
274             {
275                 ostringstream out;
276                 out << i18n("Illegal month:") << " " << month;
277                 rtn = out.str();
278             }
279     }
280
281     return rtn;
282 }
283
284
285 /*
286 ** implementaion of Interval
287 */
288
289
290 /**
291  * @brief clears the interval (make it empty).
292  */
293 void Interval::clear()
294 {
295     m_lower_bound = m_upper_bound = 0;
296 } // eo Interval::clear()
297
298
299 /**
300  * @brief tests if there is some overlapping with another interval
301  * @param other the other interval
302  * @return @a true if the two intervals have a non empty intersection.
303  */
304 bool Interval::intersects(const Interval& other) const
305 {
306     return
307         //  // other start within this:
308         (other.m_lower_bound >= m_lower_bound and other.m_lower_bound < m_upper_bound )
309         // // other end within this:
310         or (other.m_upper_bound > m_lower_bound and other.m_upper_bound <= m_upper_bound )
311         //  // other contains this
312         or (other.m_lower_bound <= m_lower_bound and other.m_upper_bound >= m_upper_bound )
313     ;
314 } // eo Interval::intersects(const Interval&)
315
316
317 /**
318  * @brief tests if the current interval (fully) contains another one.
319  * @param other the other interval.
320  * @return @a true if the current interval fully contains the other interval.
321  */
322 bool Interval::contains(const Interval& other) const
323 {
324     return  (other.m_lower_bound >= m_lower_bound)
325         and (other.m_upper_bound <= m_upper_bound)
326     ;
327 } // eo Interval::contains(const Interval& other) const
328
329
330 /*
331 ** implementation of Intervals:
332 */
333
334
335 Intervals::Intervals()
336 {
337 } // eo Intervals::Intervals
338
339
340 void Intervals::clear()
341 {
342     m_intervals.clear();
343 } // eo Intervals::clear()
344
345 /**
346  * @brief tests if one of the intervals of the list intersects with the given interval.
347  * @param other the interval to check for intersection.
348  * @return @a true if there is an intersection.
349  */
350 bool Intervals::intersects(const Interval& other) const
351 {
352     for(const_iterator it= begin();
353         it != end();
354         ++it)
355     {
356         if ( it->intersects(other) )
357         {
358             return true;
359         }
360     }
361     return false;
362 } // eo Intervals::intersects(const Interval&) const
363
364
365 /**
366  * @brief tests if we have at least one intersection with another Intervals instance.
367  * @param other the other instance.
368  * @return @a true if there is an intersection.
369  */
370 bool Intervals::intersects(const Intervals& other) const
371 {
372     for(const_iterator it= begin();
373         it != end();
374         ++it)
375     {
376         if ( other.intersects( *it ) )
377         {
378             return true;
379         }
380     }
381     return false;
382 } // eo Intervals::intersects(const Intervals&) const
383
384
385 /**
386  * @brief adds a new interval to the list.
387  * @param new_frame the new interval.
388  *
389  * Adds the interval to the list and joins overlapping intervals.
390  *
391  * @internal complexity O(n).
392  */
393 void Intervals::add(const Interval& new_frame)
394 {
395     if (not new_frame.is_valid() or new_frame.empty())
396     {
397         // well... we will not insert invalid or empty frames!
398         return;
399     }
400     for (IntervalList::iterator it= m_intervals.begin();
401          it != m_intervals.end();
402          ++it)
403     {
404         Interval& current_frame = *it;
405         if ( new_frame.m_lower_bound > current_frame.m_upper_bound )
406         {
407             // new_frame begins later than current end; go on:
408             continue;
409         }
410         // at this point: the begin of the new frame is less then the current end.
411         // now let's determine how we can insert the new frame:
412
413         if ( new_frame.m_upper_bound < current_frame.m_lower_bound )
414         {
415             // new disjoint frame; insert it before the current frame:
416             m_intervals.insert( it, new_frame );
417             // and we are done.
418             return;
419         }
420         // at this point: the end of the new frame is >= current begin. 
421         if ( new_frame.m_upper_bound <= current_frame.m_upper_bound )
422         {
423             // the end of the new frame is within our current frame; we need to combine
424             if (new_frame.m_lower_bound < current_frame.m_lower_bound)
425             {
426                 // the new interval starts earlier; we need to adjust our current frame:
427                 current_frame.m_lower_bound = new_frame.m_lower_bound;
428                 current_frame.m_changed = true;
429             }
430             // NOTE no "else" part needed since in that case our current frame already
431             // contains the new one!
432
433             // we are done:
434             return;
435         }
436         // at this point: end of new frame > end of current frame
437         // so we need to extend the current frame; at least the end.
438         // But we need to deal with intersects of following frames... *sigh*
439
440         // first the simple part: let's see if we need to move the start:
441         if ( new_frame.m_lower_bound < current_frame.m_lower_bound)
442         {
443             // yes, we need to move the start:
444             current_frame.m_lower_bound = new_frame.m_lower_bound;
445             current_frame.m_changed= true;
446         }
447
448         // now let's extend the end:
449         current_frame.m_upper_bound = new_frame.m_upper_bound;
450         current_frame.m_changed = true;
451
452         // well... let's walk through the following frames; looking for more joins...:
453         IntervalList::iterator it2 = it;
454         while(      ++(it2=it) != m_intervals.end()
455                 and current_frame.m_upper_bound >= it2->m_lower_bound
456            )
457         {
458             Interval next_frame= *it2;
459             if ( current_frame.m_upper_bound < next_frame.m_upper_bound )
460             {
461                 // in this case our end is within the next frame.
462                 // adjust our end.
463                 current_frame.m_upper_bound = next_frame.m_upper_bound;
464             }
465             // and remove the next frame since the current frame contains it (now):
466             m_intervals.erase(it2);
467         }
468         // we are done!
469         return;
470     }
471     // at this point: new frame starts later than the last frame ends
472     // append the new frame:
473     m_intervals.push_back( new_frame );
474 } // eo Intervals::add(const Interval&)
475
476
477 /**
478  * @brief subtracts a time interval from the list.
479  * @param del_frame the time interval to subtract.
480  *
481  * removes the time interval from the list; cut off parts from or remove existing
482  * intervals if they overlap.
483  *
484  * @internal complexity O(n).
485  */
486 void Intervals::sub(const Interval& del_frame)
487 {
488     if (not del_frame.is_valid() or del_frame.empty() )
489     {
490         return;
491     }
492     for (IntervalList::iterator it= m_intervals.begin();
493          it != m_intervals.end();
494          )
495     {
496         Interval& current_frame = *it;
497         if ( del_frame.m_lower_bound >= current_frame.m_upper_bound )
498         {
499             // del_frame begins later than current end; go on:
500             ++it;
501             continue;
502         }
503         // at this point: the begin of the del frame is less then the current end.
504         if ( del_frame.m_upper_bound < current_frame.m_lower_bound )
505         {
506             // end is before our start; nothing to do.
507             return;
508         }
509         // at this point: the end of the del frame is >= current begin.
510         if ( del_frame.m_upper_bound < current_frame.m_upper_bound )
511         {
512             // del frame end point is within our interval.
513             if ( del_frame.m_lower_bound > current_frame.m_lower_bound)
514             {
515                 // the del frame is within our interval... we need to split:
516                 m_intervals.insert(it, Interval( current_frame.m_lower_bound, del_frame.m_lower_bound ) );
517             }
518             // adjust start of current frame:
519             if (current_frame.m_lower_bound < del_frame.m_upper_bound)
520             {
521                 current_frame.m_lower_bound= del_frame.m_upper_bound;
522                 current_frame.m_changed= true;
523             }
524             // and we are done!
525             return;
526         }
527         // at this point the end of the del frame is >= current end
528         if ( del_frame.m_lower_bound > current_frame.m_lower_bound )
529         {
530             // a part of the current interval needs to be preserved..
531             // move the end.
532             current_frame.m_upper_bound= del_frame.m_lower_bound;
533             current_frame.m_changed= true;
534             // and continue with the next interval:
535             ++it;
536             continue;
537         }
538         // at this point; the whole frame needs to be deleted..
539         if ( it == m_intervals.begin())
540         {
541             m_intervals.erase(it);
542             it= m_intervals.begin();
543         }
544         else
545         {
546             IntervalList::iterator it2= it++;
547             m_intervals.erase(it2);
548         }
549     }
550 } // eo Intervals::sub(const Interval&)
551
552
553 /**
554  * @brief returns if we contain an interval.
555  * @param other the interval to check.
556  * @return @a true if we cover the given interval, too.
557  */
558 bool Intervals::contains(const Interval& other) const
559 {
560     for(const_iterator it= begin();
561         it != end();
562         ++it)
563     {
564         if ( it->contains( other ))
565         {
566             return true;
567         }
568     }
569     return false;
570 } // eo Intervals::contains(const Interval&) const
571
572
573 /**
574  * @brief returns if we contain an exact interval.
575  * @param other the interval to check.
576  * @return @a true if we axactly contains the given interval.
577  *
578  * @note thsi differs from contain in the way, that we return only @a true
579  * iff we have the given interval in our list; not only cover it.
580  */
581 bool Intervals::contains_exact(const Interval& other) const
582 {
583     for(const_iterator it= begin();
584         it != end();
585         ++it)
586     {
587         if ( *it == other)
588         {
589             return true;
590         }
591     }
592     return false;
593 } // eo Intervals::contains_exact(const Interval&)const
594
595
596 /**
597  * @brief returns if we contain another interval combination.
598  * @param other the intervals to check.
599  * @return @a true if we cover the given intervals, too.
600  *
601  * @internal we rely on the fact that the lists are sorted and contain
602  * disjoint intervals.
603  *
604  * So this method has a complexity of O(n).
605  */
606 bool Intervals::contains(const Intervals& other) const
607 {
608     const_iterator my_it= begin();
609     const_iterator other_it= other.begin();
610     while( my_it != end() and other_it!= other.end() )
611     {
612         // seek the first interval which contains the lower bound of the current other interval
613         while (my_it != end()
614                and my_it->m_lower_bound > other_it->m_lower_bound
615                and other_it->m_lower_bound >= my_it->m_upper_bound
616               )
617         {
618             ++my_it;
619         }
620         if (my_it == end())
621         {
622             break;
623         }
624         if (not my_it->contains( *other_it ))
625         {
626             // if we don't contain the current other; we're done:
627             return false;
628         }
629         //else check the next other interval:
630         ++other_it;
631     }
632     return (other_it == other.end());
633 } // eo Intervals::contains(const Intervals&) const
634
635
636 /**
637  * @brief combines to interval combinates for equality
638  * @param other the other instance.
639  * @return @a true if the other is equal to the current.
640  *
641  * @internal since the lists are sorted, we compare the interval lists.
642  * Thus we have a complexity of O(n).
643  */
644 bool Intervals::operator==(const Intervals& other) const
645 {
646     // since we keep sorted lists: just compare the lists :-)
647     return m_intervals == other.m_intervals;
648 } // eo Intervals::operator==(const Intervals&)
649
650
651 Intervals& Intervals::operator+=(const Interval& other)
652 {
653     add(other);
654     return *this;
655 } // eo operator+=(const Interval&)
656
657
658 Intervals& Intervals::operator-=(const Interval& other)
659 {
660     sub(other);
661     return *this;
662 } // eo operator-=(const Interval&)
663
664
665 /**
666  * @brief adds the intervals of a second instance to us.
667  * @param other the other instance.
668  * @return self reference (allow chaining).
669  *
670  * @internal since we do simple loops over the other and our intervals
671  * we have a complexity of O(n^2).
672  *
673  * @todo optimize if complexity becomes a problem.
674  */
675 Intervals& Intervals::operator+=(const Intervals& other)
676 {
677     for(const_iterator it= other.begin();
678         it != other.end();
679         ++it)
680     {
681         add( *it );
682     }
683     return *this;
684 } // eo operator+=(const Intervals&)
685
686
687 /**
688  * @brief subtracts the intervals of a second instance from us.
689  * @param other the other instance.
690  * @return self reference (allow chaining).
691  *
692  * @internal since we do simple loops over the other and our intervals
693  * we have a complexity of O(n^2).
694  *
695  * @todo optimize if complexity becomes a problem.
696  */
697 Intervals& Intervals::operator-=(const Intervals& other)
698 {
699     if (&other == this)
700     {
701         m_intervals.clear();
702     }
703     else
704     {
705         for(const_iterator it= other.begin();
706             it != other.end();
707             ++it)
708         {
709             sub( *it );
710         }
711     }
712     return *this;
713 } // eo operator-=(const Intervals&)
714
715
716
717 /*
718 ** clock funcs:
719 */
720
721
722 /**
723  * @brief fetches the value from the monotonic clock source.
724  * @param[out] seconds the seconds.
725  * @param[out] nano_seconds the nano seconds.
726  * @return @a true if the clock was successfully read.
727  */
728 bool monotonic_clock_gettime(long int& seconds, long int& nano_seconds)
729 {
730     struct timespec tp[1];
731     int res= clock_gettime (CLOCK_MONOTONIC, tp);
732     if (0 == res)
733     {
734         seconds= tp->tv_sec;
735         nano_seconds= tp->tv_nsec;
736     }
737     return (res==0);
738 } // eo monotonic_clock_gettime(long int&,long int&)
739
740
741 /**
742  * @brief fetches the value from the monotonic clock source.
743  * @return the time since system start in nanoseconds, 0 if read was unsuccessful
744  */
745 long long monotonic_clock_gettime_nano()
746 {
747     long int seconds;
748     long int nano_seconds;
749     long long nano=0;
750
751     if (monotonic_clock_gettime(seconds,nano_seconds))
752     {
753         nano=seconds;
754         nano*=1000000000LL;
755         nano+=nano_seconds;
756     }
757
758     return nano;
759 }
760
761 /**
762  * @brief fetches the value from the monotonic clock source.
763  * @param[out] seconds the seconds.
764  * @param[out] nano_seconds the nano seconds.
765  * @return @a true if the clock was successfully read.
766  */
767 bool realtime_clock_gettime(long int& seconds, long int& nano_seconds)
768 {
769     struct timespec tp[1];
770     int res= clock_gettime(CLOCK_REALTIME, tp);
771     if (0 == res)
772     {
773         seconds= tp->tv_sec;
774         nano_seconds= tp->tv_nsec;
775     }
776     return (res==0);
777 } // eo realtime_clock_gettime(long int&,long int&)
778
779
780 /*
781  * There is a discrepancy of one input character
782  * due to the lack of sign handling in strptime(3):
783  *
784  *      - strftime(3) needs the year specified as %5Y to account for the
785  *        leading dash;
786  *      - strptime(3) will not parse the leading dash with that format
787  *        but apart from that it works well.
788  */
789
790 namespace iso8601 {
791
792     /*
793      * max: -YYYYYYYYYY-MM-DDThh:mm:ssZ+zzzz ⇒ 32
794      * That is assuming the year in broken down time is an int we
795      * need to reserve ten decimal places.
796      */
797 //  static_assert (sizeof (((struct tm *)NULL)->tm_year) == 4);
798     static const size_t bufsize = 33;
799
800     enum kind {
801         d    = 0,
802         t    = 1,
803         tz   = 2,
804         dt   = 3,
805         dtz  = 4,
806         ISO8601_SIZE = 5,
807     };
808
809     /*
810      * Unfortunately the glibc strptime(3) on the Intranator trips over
811      * the length specifier in field descriptors so we can’t reuse the
812      * formatters here. This problem is fixed in newer glibc. For the time
813      * being we keep two tables of formatters and choose the appropriate
814      * at runtime.
815      */
816
817     static const char *const formatter [ISO8601_SIZE] =
818     { /* [iso8601::d  ] = */ "%4Y-%m-%d",
819       /* [iso8601::t  ] = */ "%T",
820       /* [iso8601::tz ] = */ "%TZ%z",
821       /* [iso8601::dt ] = */ "%4Y-%m-%dT%T",
822       /* [iso8601::dtz] = */ "%4Y-%m-%dT%TZ%z",
823     };
824
825     static const char *const scanner [ISO8601_SIZE] =
826     { /* [iso8601::d  ] = */ "%Y-%m-%d",
827       /* [iso8601::t  ] = */ "%T",
828       /* [iso8601::tz ] = */ "%TZ%z",
829       /* [iso8601::dt ] = */ "%Y-%m-%dT%T",
830       /* [iso8601::dtz] = */ "%Y-%m-%dT%TZ%z",
831     };
832
833     static inline const char *
834     pick_fmt (const bool date, const bool time, const bool tz, const bool scan=false)
835     {
836         const char *const  *table  = scan ? iso8601::scanner : iso8601::formatter;
837         enum iso8601::kind  format = iso8601::dtz;
838
839         if (date) {
840             if (time) {
841                 if (tz) {
842                     format = iso8601::dtz;
843                 } else {
844                     format = iso8601::dt;
845                 }
846             } else {
847                 format = iso8601::d;
848             }
849         } else if (time && tz) {
850             format = iso8601::tz;
851         } else {
852             format = iso8601::t; /* default to %T */
853         }
854
855         return table [format];
856     }
857
858 } /* [iso8601] */
859
860
861 namespace {
862
863     static inline int flip_tm_year (const int y)
864     { return (y + 1900) * -1 - 1900; }
865 } /* [namespace] */
866
867 /**
868  * @brief         Format a time structure according to ISO-8601, e. g.
869  *                “2018-01-09T10:40:00Z+0100”; see \c strftime(3) for
870  *                the details.
871  *
872  * @param tm      Time to format as broken-down \c struct tm.
873  * @param date    Include the day part ([-]YYYY-MM-DD).
874  * @param time    Include the time part (hh:mm:ss).
875  * @param tz      Include the timezone ([±]ZZZZ); only needed if
876  *                \c time is requested as well.
877  *
878  * @return        The formatted timestamp.
879  */
880 std::string format_iso8601 (const struct tm &tm, const bool date,
881                             const bool time, const bool tz)
882 {
883     struct tm tmp;
884     char buf [iso8601::bufsize] = { 0 };
885     char *start = &buf [0];
886     const char *format = iso8601::pick_fmt (date, time, tz);
887
888     memcpy (&tmp, &tm, sizeof (tmp));
889
890     if (tmp.tm_year < -1900) { /* negative year */
891         *start = '-';
892         start++;
893         tmp.tm_year = flip_tm_year (tmp.tm_year);
894     }
895
896     /*
897      * The sign is *always* handled above so the formatted string here
898      * is always one character shorter.
899      */
900     if (strftime (start, iso8601::bufsize-1, format, &tmp) == 0)
901     {
902         return std::string ();
903     }
904
905     buf [iso8601::bufsize-1] = '\0'; /* Just in case. */
906
907     return std::string (buf);
908 }
909
910 typedef struct tm * (*time_breakdown_fn) (const time_t *, struct tm *);
911
912 /**
913  * @brief         Format a UNIX timestamp according to ISO-8601. Converts
914  *                to broken down time first.
915  *
916  * @param t       Time to format as broken-down \c struct tm.
917  * @param date    Include the day part ([-]YYYY-MM-DD).
918  * @param time    Include the time part (hh:mm:ss).
919  * @param tz      Include the timezone ([±]ZZZZ); only heeded if
920  *                \c time is requested as well.
921  *
922  * @return        The formatted timestamp.
923  */
924 std::string format_iso8601 (time_t t, const bool utc, const bool date,
925                             const bool time, const bool tz)
926 {
927     time_breakdown_fn breakdown = utc ? gmtime_r : localtime_r;
928     struct tm tm;
929
930     errno = 0;
931     if (breakdown (&t, &tm) == NULL) {
932         return std::string ("error analyzing timestamp: ") + strerror (errno);
933     }
934
935     return format_iso8601 (tm, date, time, tz);
936 }
937
938 /**
939  * @brief         Read a ISO-8601 formatted date stamp into broken down time.
940  *
941  * @param s       String containing the timestamp.
942  *
943  * @return        \c boost::none if the input string was \c NULL or malformed,
944  *                an optional \c struct tm with the extracted values otherwise.
945  */
946 boost::optional<struct tm>
947 scan_iso8601 (const char *s,
948               const bool date, const bool time, const bool tz) NOEXCEPT
949 {
950     struct tm tm;
951     const char *format = iso8601::pick_fmt (date, time, tz, true);
952     const char *start = s;
953     bool negyear = false;
954
955     if (s == NULL) {
956         return boost::none;
957     }
958
959     switch (s [0]) {
960         case '\0': {
961             return boost::none;
962             break;
963         }
964         /*
965          * Contrary to what the man page indicates, strptime(3) is *not*
966          * the inverse operation of strftime(3)! The later correctly formats
967          * negative year numbers with the %F modifier wheres the former trips
968          * over the sign character.
969          */
970         case '-': {
971             negyear = true;
972             start++;
973             break;
974         }
975         default: {
976             break;
977         }
978     }
979
980     memset (&tm, 0, sizeof (tm));
981
982     if (strptime (start, format, &tm) == NULL) {
983         return boost::none;
984     }
985
986     if (negyear) {
987         tm.tm_year = flip_tm_year (tm.tm_year);
988     }
989
990     return tm;
991 }
992
993 /**
994  * @brief         Format a \c struct timespec in the schema established by
995  *                time(1): “3m14.159s”.
996  *
997  * @param ts      The time spec to format.
998  *
999  * @return        \c boost:none in case of error during formatting, an optional
1000  *                \c std::string otherwise.
1001  */
1002 boost::optional<std::string>
1003 format_min_sec_msec (const struct timespec &ts)
1004 {
1005     char ms [4] = { '\0', '\0', '\0', '\0' };
1006
1007     if (snprintf (ms, 4, "%.3ld", ts.tv_nsec / 1000000) < 0) {
1008         return boost::none;
1009     }
1010
1011     const time_t min = ts.tv_sec / 60;
1012     const time_t sec = ts.tv_sec - min * 60;
1013
1014     return I2n::to_string (min) + "m"
1015          + I2n::to_string (sec) + "."
1016          + ms + "s"
1017          ;
1018 }
1019
1020 namespace I2n {
1021
1022 namespace clock {
1023
1024     namespace {
1025
1026         /**
1027          * @brief         <b>For internal use only</b>. Translates clock
1028          *                specification flags to kernel clock types.
1029          *
1030          * @param id      Master clock id: \c mono, \c real, \c boot, or \c
1031          *                cpu.
1032          * @param var     Variant of clock if appropriate: \c raw, \c exact, \c
1033          *                process, or \c thread. Use \c dflt for the base
1034          *                variant.
1035          *
1036          * @return        The clock id for using with kernel APIs.
1037          */
1038         static inline clockid_t
1039         clockid_of_flags (const enum type::id      id,
1040                           const enum type::variant var) NOEXCEPT
1041         {
1042             clockid_t cid = CLOCK_MONOTONIC_COARSE;
1043
1044             switch (id) {
1045
1046                 default:
1047                 case type::mono: {
1048                     switch (var) {
1049                         default: {
1050                             break;
1051                         }
1052                         case type::raw: {
1053                             cid = CLOCK_MONOTONIC_RAW;
1054                             break;
1055                         }
1056                         case type::exact: {
1057                             cid = CLOCK_MONOTONIC;
1058                             break;
1059                         }
1060                     }
1061                     break;
1062                 }
1063
1064                 case type::real: {
1065                     if (var == type::exact) {
1066                         cid = CLOCK_REALTIME;
1067                     } else {
1068                         cid = CLOCK_REALTIME_COARSE;
1069                     }
1070                     break;
1071                 }
1072
1073                 case type::boot: {
1074                     cid = CLOCK_BOOTTIME;
1075                     break;
1076                 }
1077
1078                 case type::cpu: {
1079                     if (var == type::thread) {
1080                         cid = CLOCK_THREAD_CPUTIME_ID;
1081                     } else {
1082                         cid = CLOCK_PROCESS_CPUTIME_ID;
1083                     }
1084                     break;
1085                 }
1086             } /* [switch id] */
1087
1088             return cid;
1089         }
1090
1091         static const struct timespec zero_time = { 0, 0 };
1092
1093     } /* [namespace] */
1094
1095     Time::Time (const enum type::id id,
1096                 const enum type::variant var) NOEXCEPT
1097         : value   (zero_time)
1098         , id      (id)
1099         , variant (var)
1100         , err     (0)
1101     { }
1102
1103     /*
1104      * Ctor from *struct tm*. On 32 bit systems the conversion to *time_t* will
1105      * fail with years outside the range from epoch to 2038.
1106      */
1107     Time::Time (const struct tm          &tm,
1108                 const enum type::id       id,
1109                 const enum type::variant  var)
1110     {
1111         struct tm tmp_tm; /* dummy for mktime(3) */
1112         Time   tmp_time;
1113
1114         memcpy (&tmp_tm, &tm, sizeof (tmp_tm));
1115
1116         errno = 0;
1117         const time_t t = mktime (&tmp_tm);
1118         if (t == - 1) { /* Glibc does not set errno on out-of-range here! */
1119             const char *datestr = asctime (&tm);
1120             throw conversion_error (errno,
1121                                     std::string ("mktime: from struct tm {")
1122                                     + std::string (datestr, 0, strlen(datestr)-1)
1123                                     + "}");
1124         }
1125
1126         tmp_time = Time (t, 0l, id, var);
1127
1128         this->swap (tmp_time);
1129     }
1130
1131     int64_t
1132     Time::as_nanosec (void) const NOEXCEPT
1133     {
1134         return int64_t (this->value.tv_sec) * TIME_CONST_FACTOR_NANO
1135              + this->value.tv_nsec;
1136     }
1137
1138     long
1139     Time::as_nanosec_L (void) const NOEXCEPT /* likely to overflow */
1140     { return static_cast<long>(this->as_nanosec ()); }
1141
1142     Time &
1143     Time::operator= (Time t2) NOEXCEPT
1144     {
1145         this->swap (t2);
1146
1147         return *this;
1148     }
1149
1150     /*
1151      * @note    This operator is an up-assignment from a type containing less
1152      *          information than the structure assigned from. Since the
1153      *          operator can only be two valued we must normalize the remaining
1154      *          fields to the default clock. When assigning from non-default
1155      *          clocks, use the appropriate constructor and pass it the desired
1156      *          id and variant so as to assign the result.
1157      */
1158     Time &
1159     Time::operator= (struct timespec ts) NOEXCEPT
1160     {
1161         std::swap (this->value, ts);
1162         this->id      = clock::type::mono;
1163         this->variant = clock::type::dflt;
1164         this->err     = 0;
1165
1166         return *this;
1167     }
1168
1169     void
1170     Time::unset (void) NOEXCEPT
1171     { this->value = zero_time; }
1172
1173     bool
1174     Time::set (void) NOEXCEPT
1175     {
1176         struct timespec now;
1177
1178         errno = 0;
1179         if (clock_gettime (clockid_of_flags (this->id, this->variant), &now)
1180             == -1)
1181         {
1182             this->err = errno;
1183             this->unset ();
1184
1185             return false;
1186         }
1187         this->err   = 0;
1188         this->value = now;
1189
1190         return true;
1191     }
1192
1193     Time &
1194     Time::add (const time_t sec, const long nsec) NOEXCEPT
1195     {
1196         this->value.tv_sec  += sec;
1197         this->value.tv_nsec += nsec;
1198
1199         this->carry_nsec ();
1200
1201         return *this;
1202     }
1203
1204     Time &
1205     Time::subtract (const time_t sec, const long nsec) NOEXCEPT
1206     {
1207         this->value.tv_sec  -= sec;
1208         this->value.tv_nsec -= nsec;
1209
1210         this->carry_nsec ();
1211
1212         return *this;
1213     }
1214
1215     Time &
1216     Time::scale (const int64_t factor) NOEXCEPT
1217     {
1218         this->value.tv_sec  *= factor;
1219         this->value.tv_nsec *= factor;
1220
1221         this->carry_nsec ();
1222
1223         return *this;
1224     }
1225
1226     /*
1227      * Below division code purposely does not attempt to handle divide-
1228      * by-zero just as any other C++ division function does. It is up to
1229      * the caller to ensure that the divisor is not zero.
1230      */
1231     Time &
1232     Time::divide (const int64_t divisor) NOEXCEPT
1233     {
1234         const long   sec  = static_cast<long>    (this->value.tv_sec );
1235         int64_t      nsec = static_cast<int64_t> (this->value.tv_nsec);
1236         const ldiv_t div  = ldiv (sec, divisor);
1237
1238         if (div.rem != 0) {
1239             nsec += div.rem * TIME_CONST_FACTOR_NANO;
1240         }
1241
1242         nsec /= divisor;
1243
1244         this->value.tv_sec  = static_cast<time_t> (div.quot);
1245         this->value.tv_nsec = static_cast<long>   (nsec);
1246
1247         this->carry_nsec ();
1248
1249         return *this;
1250     }
1251
1252     /**
1253      * @brief         Format timestamp according to the ISO standard rules.
1254      *
1255      * @param utc     Whether to normalize the timestamp to UTC or local time.
1256      * @param date    Whether to include the date (%F).
1257      * @param time    Whether to include the time (%T).
1258      * @param tz      Whether to include the UTC offset (%z).
1259      *
1260      * @return        \c none if the formatting operation failed, the
1261      *                formatted timestamp otherwise.
1262      *
1263      * @note          The standard allows for extending the format using
1264      *                a fractional component. However, this is subject to
1265      *                local conventions so we don’t support it. For more
1266      *                than seconds granularity use a better suited format
1267      *                like LDAP Generalized time instead.
1268      */
1269     boost::optional<std::string>
1270     Time::format_iso8601 (const bool utc,
1271                           const bool date,
1272                           const bool time,
1273                           const bool tz) const
1274     {
1275         time_breakdown_fn breakdown = utc ? gmtime_r : localtime_r;
1276         struct tm tm;
1277
1278         if (breakdown (&this->value.tv_sec, &tm) == NULL) {
1279             return boost::none;
1280         }
1281
1282         return ::format_iso8601 (tm, date, time, tz);
1283     }
1284
1285     std::string
1286     Time::make_nice_time (void) const
1287     {
1288         /* XXX the cast below results in loss of precision with 64 bit time_t! */
1289         return ::make_nice_time (static_cast<int> (this->value.tv_sec));
1290     }
1291
1292     std::string
1293     Time::format_full_time (void) const
1294     { return ::format_full_time (this->value.tv_sec); }
1295
1296     std::string
1297     Time::format_date (void) const
1298     { return ::format_date (this->value.tv_sec); }
1299
1300     /**
1301      * @brief         Obtain the current time wrt. the given
1302      *                clock variants.
1303      *
1304      * @param id      Clock id.
1305      * @param var     Clock variant.
1306      *
1307      * @return        \c none if the underlying \c clock_gettime() operation
1308      *                failed, a fully initialized \c struct Time otherwise.
1309      */
1310     boost::optional<Time>
1311     now (const enum type::id id, const enum type::variant var) NOEXCEPT
1312     {
1313         Time ret (id, var);
1314
1315         if (!ret.set ()) {
1316             return boost::none;
1317         }
1318
1319         return ret;
1320     }
1321
1322     Time
1323     zero (const enum type::id id, const enum type::variant var) NOEXCEPT
1324     { return Time (id, var); }
1325
1326     /**
1327      * @brief         Standard three-way comparison for \c struct Time
1328      *                relying on strict total ordering.
1329      *
1330      * @param t1      Comparand.
1331      * @param t2      Comparand.
1332      *
1333      * @return        -1, 0, 1 depending on whether t1 is less-than, equal,
1334      *                or greater than t2.
1335      *
1336      * @note          This should be used to implement the spaceship operator
1337      *                (P0515R0) when we get a new compiler.
1338      */
1339     int
1340     compare (const Time &t1, const Time &t2) NOEXCEPT
1341     {
1342         if (t1.value.tv_sec < t2.value.tv_sec) {
1343             return -1;
1344         }
1345
1346         if (t1.value.tv_sec > t2.value.tv_sec) {
1347             return 1;
1348         }
1349
1350         if (t1.value.tv_nsec < t2.value.tv_nsec) {
1351             return -1;
1352         }
1353
1354         if (t1.value.tv_nsec > t2.value.tv_nsec) {
1355             return 1;
1356         }
1357
1358         return 0;
1359     }
1360
1361     /**
1362      * @brief         Interpret string as timestamp according to the ISO
1363      *                standard rules.
1364      *
1365      *                This is the inverse operation of \c format_iso8601().
1366      *
1367      * @param s       Input string to read. The entire string is interpreted
1368      *                and it must not contain any trailing data.
1369      * @param date    Whether to parse the date (%F).
1370      * @param time    Whether to parse the time (%T).
1371      * @param tz      Whether to parse the UTC offset (%z).
1372      * @param id      Clock id to assign the result.
1373      * @param var     Clock variant to assign the result.
1374      *
1375      * @return        \c none if the input could not be parsed according to
1376      *                ISO rules, a \c struct Time otherwise.
1377      */
1378     boost::optional<Time>
1379     time_of_iso8601 (const std::string        &s,
1380                      const bool                date,
1381                      const bool                time,
1382                      const bool                tz,
1383                      const enum type::id       id,
1384                      const enum type::variant  var) NOEXCEPT
1385     {
1386         boost::optional<struct tm> tm = scan_iso8601 (s, date, time, tz);
1387
1388         if (!tm) {
1389             return boost::none;
1390         }
1391
1392         try {
1393             return Time (*tm, id, var);
1394         }
1395         catch (conversion_error &_unused) { }
1396
1397         return boost::none;
1398     }
1399
1400 } /* [namespace clock] */
1401
1402 } /* [namespace I2n] */