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