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