apply UTC offset during timestamp conversion
[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 ] = */ "%T%z",
817       /* [iso8601::dt ] = */ "%4Y-%m-%dT%T",
818       /* [iso8601::dtz] = */ "%4Y-%m-%dT%T%z",
819     };
820
821     static const char *const scanner [ISO8601_SIZE] =
822     { /* [iso8601::d  ] = */ "%Y-%m-%d",
823       /* [iso8601::t  ] = */ "%T",
824       /* [iso8601::tz ] = */ "%T%z",
825       /* [iso8601::dt ] = */ "%Y-%m-%dT%T",
826       /* [iso8601::dtz] = */ "%Y-%m-%dT%T%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 latter 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
1114         time_t t = mktime (&tmp_tm);
1115         if (t == - 1) { /* Glibc does not set errno on out-of-range here! */
1116             const char *datestr = asctime (&tm);
1117             throw conversion_error (errno,
1118                                     std::string ("mktime: from struct tm {")
1119                                     + std::string (datestr, 0, strlen(datestr)-1)
1120                                     + "}");
1121         }
1122
1123         /*
1124          * The timezone shenanigans are there to force we end up using UTC
1125          * internally. The trouble with mktime(3) is that the unix timestamp
1126          * is defined as â€śseconds since epoch” but â€śexpressed as local time”.
1127          */
1128         t += tm.tm_gmtoff;
1129         t -= timezone;
1130
1131         tmp_time = Time (t, 0l, id, var);
1132
1133         this->swap (tmp_time);
1134     }
1135
1136     int64_t
1137     Time::as_nanosec (void) const NOEXCEPT
1138     {
1139         return int64_t (this->value.tv_sec) * TIME_CONST_FACTOR_NANO
1140              + this->value.tv_nsec;
1141     }
1142
1143     long
1144     Time::as_nanosec_L (void) const NOEXCEPT /* likely to overflow */
1145     { return static_cast<long>(this->as_nanosec ()); }
1146
1147     Time &
1148     Time::operator= (Time t2) NOEXCEPT
1149     {
1150         this->swap (t2);
1151
1152         return *this;
1153     }
1154
1155     /*
1156      * @note    This operator is an up-assignment from a type containing less
1157      *          information than the structure assigned from. Since the
1158      *          operator can only be two valued we must normalize the remaining
1159      *          fields to the default clock. When assigning from non-default
1160      *          clocks, use the appropriate constructor and pass it the desired
1161      *          id and variant so as to assign the result.
1162      */
1163     Time &
1164     Time::operator= (struct timespec ts) NOEXCEPT
1165     {
1166         std::swap (this->value, ts);
1167         this->id      = clock::type::mono;
1168         this->variant = clock::type::dflt;
1169         this->err     = 0;
1170
1171         return *this;
1172     }
1173
1174     void
1175     Time::unset (void) NOEXCEPT
1176     { this->value = zero_time; }
1177
1178     bool
1179     Time::set (void) NOEXCEPT
1180     {
1181         struct timespec now;
1182
1183         errno = 0;
1184         if (clock_gettime (clockid_of_flags (this->id, this->variant), &now)
1185             == -1)
1186         {
1187             this->err = errno;
1188             this->unset ();
1189
1190             return false;
1191         }
1192         this->err   = 0;
1193         this->value = now;
1194
1195         return true;
1196     }
1197
1198     Time &
1199     Time::add (const time_t sec, const long nsec) NOEXCEPT
1200     {
1201         this->value.tv_sec  += sec;
1202         this->value.tv_nsec += nsec;
1203
1204         this->carry_nsec ();
1205
1206         return *this;
1207     }
1208
1209     Time &
1210     Time::subtract (const time_t sec, const long nsec) NOEXCEPT
1211     {
1212         this->value.tv_sec  -= sec;
1213         this->value.tv_nsec -= nsec;
1214
1215         this->carry_nsec ();
1216
1217         return *this;
1218     }
1219
1220     Time &
1221     Time::scale (const int64_t factor) NOEXCEPT
1222     {
1223         this->value.tv_sec  *= factor;
1224         this->value.tv_nsec *= factor;
1225
1226         this->carry_nsec ();
1227
1228         return *this;
1229     }
1230
1231     /*
1232      * Below division code purposely does not attempt to handle divide-
1233      * by-zero just as any other C++ division function does. It is up to
1234      * the caller to ensure that the divisor is not zero.
1235      */
1236     Time &
1237     Time::divide (const int64_t divisor) NOEXCEPT
1238     {
1239         const long   sec  = static_cast<long>    (this->value.tv_sec );
1240         int64_t      nsec = static_cast<int64_t> (this->value.tv_nsec);
1241         const ldiv_t div  = ldiv (sec, divisor);
1242
1243         if (div.rem != 0) {
1244             nsec += div.rem * TIME_CONST_FACTOR_NANO;
1245         }
1246
1247         nsec /= divisor;
1248
1249         this->value.tv_sec  = static_cast<time_t> (div.quot);
1250         this->value.tv_nsec = static_cast<long>   (nsec);
1251
1252         this->carry_nsec ();
1253
1254         return *this;
1255     }
1256
1257     /**
1258      * @brief         Format timestamp according to the ISO standard rules.
1259      *
1260      * @param utc     Whether to normalize the timestamp to UTC or local time.
1261      * @param date    Whether to include the date (%F).
1262      * @param time    Whether to include the time (%T).
1263      * @param tz      Whether to include the UTC offset (%z).
1264      *
1265      * @return        \c none if the formatting operation failed, the
1266      *                formatted timestamp otherwise.
1267      *
1268      * @note          The standard allows for extending the format using
1269      *                a fractional component. However, this is subject to
1270      *                local conventions so we don’t support it. For more
1271      *                than seconds granularity use a better suited format
1272      *                like LDAP Generalized time instead.
1273      */
1274     boost::optional<std::string>
1275     Time::format_iso8601 (const bool utc,
1276                           const bool date,
1277                           const bool time,
1278                           const bool tz) const
1279     {
1280         time_breakdown_fn breakdown = utc ? gmtime_r : localtime_r;
1281         struct tm tm;
1282
1283         if (breakdown (&this->value.tv_sec, &tm) == NULL) {
1284             return boost::none;
1285         }
1286
1287         return ::format_iso8601 (tm, date, time, tz);
1288     }
1289
1290     std::string
1291     Time::make_nice_time (void) const
1292     {
1293         /* XXX the cast below results in loss of precision with 64 bit time_t! */
1294         return ::make_nice_time (static_cast<int> (this->value.tv_sec));
1295     }
1296
1297     std::string
1298     Time::format_full_time (void) const
1299     { return ::format_full_time (this->value.tv_sec); }
1300
1301     std::string
1302     Time::format_date (void) const
1303     { return ::format_date (this->value.tv_sec); }
1304
1305     /**
1306      * @brief         Obtain the current time wrt. the given
1307      *                clock variants.
1308      *
1309      * @param id      Clock id.
1310      * @param var     Clock variant.
1311      *
1312      * @return        \c none if the underlying \c clock_gettime() operation
1313      *                failed, a fully initialized \c struct Time otherwise.
1314      */
1315     boost::optional<Time>
1316     now (const enum type::id id, const enum type::variant var) NOEXCEPT
1317     {
1318         Time ret (id, var);
1319
1320         if (!ret.set ()) {
1321             return boost::none;
1322         }
1323
1324         return ret;
1325     }
1326
1327     Time
1328     zero (const enum type::id id, const enum type::variant var) NOEXCEPT
1329     { return Time (id, var); }
1330
1331     /**
1332      * @brief         Standard three-way comparison for \c struct Time
1333      *                relying on strict total ordering.
1334      *
1335      * @param t1      Comparand.
1336      * @param t2      Comparand.
1337      *
1338      * @return        -1, 0, 1 depending on whether t1 is less-than, equal,
1339      *                or greater than t2.
1340      *
1341      * @note          This should be used to implement the spaceship operator
1342      *                (P0515R0) when we get a new compiler.
1343      */
1344     int
1345     compare (const Time &t1, const Time &t2) NOEXCEPT
1346     {
1347         if (t1.value.tv_sec < t2.value.tv_sec) {
1348             return -1;
1349         }
1350
1351         if (t1.value.tv_sec > t2.value.tv_sec) {
1352             return 1;
1353         }
1354
1355         if (t1.value.tv_nsec < t2.value.tv_nsec) {
1356             return -1;
1357         }
1358
1359         if (t1.value.tv_nsec > t2.value.tv_nsec) {
1360             return 1;
1361         }
1362
1363         return 0;
1364     }
1365
1366     /**
1367      * @brief         Interpret string as timestamp according to the ISO
1368      *                standard rules.
1369      *
1370      *                This is the inverse operation of \c format_iso8601().
1371      *
1372      * @param s       Input string to read. The entire string is interpreted
1373      *                and it must not contain any trailing data.
1374      * @param date    Whether to parse the date (%F).
1375      * @param time    Whether to parse the time (%T).
1376      * @param tz      Whether to parse the UTC offset (%z).
1377      * @param id      Clock id to assign the result.
1378      * @param var     Clock variant to assign the result.
1379      *
1380      * @return        \c none if the input could not be parsed according to
1381      *                ISO rules, a \c struct Time otherwise.
1382      */
1383     boost::optional<Time>
1384     time_of_iso8601 (const std::string        &s,
1385                      const bool                date,
1386                      const bool                time,
1387                      const bool                tz,
1388                      const enum type::id       id,
1389                      const enum type::variant  var) NOEXCEPT
1390     {
1391         boost::optional<struct tm> tm = scan_iso8601 (s, date, time, tz);
1392
1393         if (!tm) {
1394             return boost::none;
1395         }
1396
1397         try {
1398             return Time (*tm, id, var);
1399         }
1400         catch (conversion_error &_unused) { }
1401
1402         return boost::none;
1403     }
1404
1405 } /* [namespace clock] */
1406
1407 } /* [namespace I2n] */