document clockid_t helper
[libi2ncommon] / src / timefunc.cpp
CommitLineData
0e23f538
TJ
1/*
2The software in this package is distributed under the GNU General
3Public License version 2 (with a special exception described below).
4
5A copy of GNU General Public License (GPL) is included in this distribution,
6in the file COPYING.GPL.
7
8As a special exception, if other files instantiate templates or use macros
9or inline functions from this file, or you compile this file and link it
10with other works to produce a work based on this file, this file
11does not by itself cause the resulting work to be covered
12by the GNU General Public License.
13
14However the source code for this file must still be made available
15in accordance with section (3) of the GNU General Public License.
16
17This exception does not invalidate any other reasons why a work based
18on this file might be covered by the GNU General Public License.
19*/
1b5dfd98
TJ
20/** @file
21 * @brief time related functions.
22 *
23 * @copyright Copyright © 2001-2008 by Intra2net AG
1b5dfd98
TJ
24 *
25 */
26
9f39641d 27#include <cstdio>
d70f7269 28#include <errno.h>
e93545dd
GE
29#include <string>
30#include <sstream>
31#include <iostream>
32#include <iomanip>
f1499910
GE
33#include <bitset>
34#include <stdexcept>
1b5dfd98
TJ
35#include <iterator>
36#include <algorithm>
e93545dd
GE
37
38#include <time.h>
96d0be2e 39#include <unistd.h>
5efd35b1 40#include <string.h>
e93545dd
GE
41#include <sys/timeb.h>
42
43#include <timefunc.hxx>
44#include <i18n.h>
45
96d0be2e
TJ
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
e93545dd
GE
58using namespace std;
59
60double 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
dad9e26f 73time_t date_to_seconds(const std::string &date)
e93545dd 74{
dad9e26f
GE
75 time_t rtn = 0;
76 int year = -1, month = -1, day = -1;
e93545dd
GE
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;
4cf3676b 101
e93545dd 102 struct tm tm_struct;
4cf3676b 103 memset(&tm_struct, 0, sizeof(struct tm));
e93545dd
GE
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
113string make_nice_time(int seconds)
114{
115 ostringstream out;
116
117 int days=seconds/86400;
118 seconds%=86400;
119
c0368918
GE
120 int hours,minutes;
121 split_daysec(seconds,&hours,&minutes,&seconds);
122
c7bd7a65
CH
123 if (days>0)
124 out << days << " " << i18n_plural("day", "days", days) << ", ";
e93545dd
GE
125
126 out << setfill('0');
127 out << setw(2) << hours << ':' << setw(2) << minutes << ':' << setw(2) << seconds;
128
129 return out.str();
130}
131
7839bd53 132string format_full_time(time_t seconds)
e93545dd
GE
133{
134 char buf[50];
135 memset (buf, 0, 50);
532d6b3a
TJ
136 struct tm ta;
137 if (localtime_r((time_t *)&seconds, &ta) == NULL)
138 memset (&ta, 0, sizeof(struct tm));
e93545dd 139
532d6b3a 140 strftime (buf, 49, "%d.%m.%Y %H:%M", &ta);
e93545dd
GE
141 return string(buf);
142}
f1499910 143
25f3d405
CH
144string 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
87869870
GE
156void 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
c0368918
GE
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 */
182void 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
200std::string output_hour_minute(int hour, int minute, bool h_for_00, int seconds)
2c66f490
GE
201{
202 ostringstream out;
203
204 if (hour >= 0 && hour < 10)
205 out << '0';
206 out << hour;
207
c0368918 208 if (!h_for_00 || minute != 0 || seconds > 0)
2c66f490
GE
209 {
210 out << ':';
fe223928 211 if (minute >= 0 && minute < 10)
2c66f490
GE
212 out << '0';
213 out << minute;
214 }
215 else
216 out << 'h';
217
c0368918
GE
218 if (seconds > 0)
219 {
220 out << ':';
221 if (seconds > 0 && seconds < 10)
222 out << '0';
223 out << seconds;
224 }
225
2c66f490
GE
226 return out.str();
227}
4e157d1d
TJ
228
229string 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}
1b5dfd98
TJ
279
280
281/*
282** implementaion of Interval
283*/
284
d181c3bc
TJ
285
286/**
287 * @brief clears the interval (make it empty).
288 */
289void Interval::clear()
290{
291 m_lower_bound = m_upper_bound = 0;
292} // eo Interval::clear()
293
294
1b5dfd98
TJ
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 */
300bool 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 */
318bool 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
331Intervals::Intervals()
332{
333} // eo Intervals::Intervals
334
335
d181c3bc
TJ
336void Intervals::clear()
337{
338 m_intervals.clear();
339} // eo Intervals::clear()
340
1b5dfd98
TJ
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 */
346bool 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 */
366bool 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.
ebc3b584
TJ
386 *
387 * @internal complexity O(n).
1b5dfd98
TJ
388 */
389void 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;
80f30818 424 current_frame.m_changed = true;
1b5dfd98
TJ
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;
80f30818 441 current_frame.m_changed= true;
1b5dfd98
TJ
442 }
443
444 // now let's extend the end:
445 current_frame.m_upper_bound = new_frame.m_upper_bound;
80f30818 446 current_frame.m_changed = true;
1b5dfd98
TJ
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.
ebc3b584
TJ
479 *
480 * @internal complexity O(n).
1b5dfd98
TJ
481 */
482void 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:
80f30818
TJ
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 }
1b5dfd98
TJ
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;
80f30818 529 current_frame.m_changed= true;
1b5dfd98
TJ
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 */
554bool 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/**
e156de7c
TJ
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 */
577bool 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/**
1b5dfd98
TJ
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.
ebc3b584
TJ
599 *
600 * So this method has a complexity of O(n).
1b5dfd98
TJ
601 */
602bool 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());
ebc3b584 629} // eo Intervals::contains(const Intervals&) const
1b5dfd98
TJ
630
631
ebc3b584
TJ
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 */
1b5dfd98
TJ
640bool 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
647Intervals& Intervals::operator+=(const Interval& other)
648{
649 add(other);
650 return *this;
651} // eo operator+=(const Interval&)
652
653
654Intervals& Intervals::operator-=(const Interval& other)
655{
656 sub(other);
657 return *this;
658} // eo operator-=(const Interval&)
659
660
ebc3b584
TJ
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 */
1b5dfd98
TJ
671Intervals& 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
ebc3b584
TJ
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 */
1b5dfd98
TJ
693Intervals& 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&)
96d0be2e
TJ
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 */
724bool monotonic_clock_gettime(long int& seconds, long int& nano_seconds)
725{
726 struct timespec tp[1];
4b1afed1 727 int res= clock_gettime (CLOCK_MONOTONIC, tp);
96d0be2e
TJ
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.
b7e17426
GE
739 * @return the time since system start in nanoseconds, 0 if read was unsuccessful
740 */
741long 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.
96d0be2e
TJ
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 */
763bool realtime_clock_gettime(long int& seconds, long int& nano_seconds)
764{
765 struct timespec tp[1];
4b1afed1 766 int res= clock_gettime(CLOCK_REALTIME, tp);
96d0be2e
TJ
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
2795e39c
PG
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 */
d70f7269 785
214a748a
PG
786namespace 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 */
d70f7269 812
214a748a
PG
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 }
d70f7269 842 } else {
214a748a 843 format = iso8601::d;
d70f7269 844 }
214a748a
PG
845 } else if (time && tz) {
846 format = iso8601::tz;
d70f7269 847 } else {
214a748a 848 format = iso8601::t; /* default to %T */
d70f7269 849 }
214a748a
PG
850
851 return table [format];
d70f7269
PG
852 }
853
214a748a 854} /* [iso8601] */
2795e39c 855
2795e39c 856
214a748a 857namespace {
2795e39c
PG
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).
fe377737 871 * @param tz Include the timezone ([±]ZZZZ); only needed if
2795e39c
PG
872 * \c time is requested as well.
873 *
874 * @return The formatted timestamp.
875 */
876std::string format_iso8601 (const struct tm &tm, const bool date,
877 const bool time, const bool tz)
878{
879 struct tm tmp;
214a748a 880 char buf [iso8601::bufsize] = { 0 };
2795e39c 881 char *start = &buf [0];
214a748a 882 const char *format = iso8601::pick_fmt (date, time, tz);
2795e39c
PG
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 /*
fe377737 893 * The sign is *always* handled above so the formatted string here
2795e39c 894 * is always one character shorter.
cb5f6477
PG
895 */
896 if (strftime (start, iso8601::bufsize-1, format, &tmp) == 0)
897 {
898 return std::string ();
899 }
d70f7269 900
cb5f6477 901 buf [iso8601::bufsize-1] = '\0'; /* Just in case. */
d70f7269
PG
902
903 return std::string (buf);
904}
905
d70f7269
PG
906typedef 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 */
920std::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
2795e39c
PG
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 */
942boost::optional<struct tm>
943scan_iso8601 (const char *s,
944 const bool date, const bool time, const bool tz) NOEXCEPT
945{
946 struct tm tm;
214a748a 947 const char *format = iso8601::pick_fmt (date, time, tz, true);
2795e39c
PG
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
9f39641d
PG
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 */
998boost::optional<std::string>
999format_min_sec_msec (const struct timespec &ts)
1000{
1001 char ms [4] = { '\0', '\0', '\0', '\0' };
1002
fe377737 1003 if (snprintf (ms, 4, "%.3ld", ts.tv_nsec / 1000000) < 0) {
9f39641d
PG
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}
d70f7269 1015
8b5814e2
PG
1016namespace I2n {
1017
1018namespace clock {
1019
1020 namespace {
1021
76a5f03b
PG
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 */
8b5814e2
PG
1034 static inline clockid_t
1035 clockid_of_flags (const enum type::id id,
e36ca33c 1036 const enum type::variant var) NOEXCEPT
8b5814e2
PG
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: {
3d690dea 1070 cid = CLOCK_BOOTTIME;
8b5814e2
PG
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
8b5814e2
PG
1089 } /* [namespace] */
1090
e36ca33c
PG
1091 Time::Time (const enum type::id id,
1092 const enum type::variant var) NOEXCEPT
8b5814e2
PG
1093 : value (zero_time)
1094 , id (id)
1095 , variant (var)
1096 , err (0)
1097 { }
1098
cf960f73
PG
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 */
2795e39c
PG
1103 Time::Time (const struct tm &tm,
1104 const enum type::id id,
cf960f73 1105 const enum type::variant var)
2795e39c
PG
1106 {
1107 struct tm tmp_tm; /* dummy for mktime(3) */
1108 Time tmp_time;
1109
1110 memcpy (&tmp_tm, &tm, sizeof (tmp_tm));
cf960f73
PG
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);
2795e39c
PG
1123
1124 this->swap (tmp_time);
1125 }
1126
8b5814e2 1127 int64_t
e36ca33c
PG
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 }
8b5814e2
PG
1133
1134 long
e36ca33c 1135 Time::as_nanosec_L (void) const NOEXCEPT /* likely to overflow */
8b5814e2
PG
1136 { return static_cast<long>(this->as_nanosec ()); }
1137
e36ca33c
PG
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
8b5814e2 1157 void
e36ca33c 1158 Time::unset (void) NOEXCEPT
8b5814e2
PG
1159 { this->value = zero_time; }
1160
1161 bool
e36ca33c 1162 Time::set (void) NOEXCEPT
8b5814e2
PG
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
e36ca33c
PG
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 &
c42fd3b3 1204 Time::scale (const int64_t factor) NOEXCEPT
e36ca33c
PG
1205 {
1206 this->value.tv_sec *= factor;
1207 this->value.tv_nsec *= factor;
1208
1209 this->carry_nsec ();
1210
1211 return *this;
1212 }
1213
c42fd3b3
PG
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
72acd54c
PG
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
9f39641d 1256 std::string
72acd54c
PG
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
9f39641d 1263 std::string
72acd54c
PG
1264 Time::format_full_time (void) const
1265 { return ::format_full_time (this->value.tv_sec); }
1266
9f39641d 1267 std::string
72acd54c
PG
1268 Time::format_date (void) const
1269 { return ::format_date (this->value.tv_sec); }
1270
8b5814e2 1271 boost::optional<Time>
e36ca33c 1272 now (const enum type::id id, const enum type::variant var) NOEXCEPT
8b5814e2 1273 {
e36ca33c 1274 Time ret (id, var);
8b5814e2
PG
1275
1276 if (!ret.set ()) {
1277 return boost::none;
1278 }
1279
1280 return ret;
1281 }
1282
e36ca33c
PG
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
2795e39c
PG
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
cf960f73
PG
1323 try {
1324 return Time (*tm, id, var);
1325 }
1326 catch (conversion_error &_unused) { }
1327
1328 return boost::none;
2795e39c
PG
1329 }
1330
8b5814e2
PG
1331} /* [namespace clock] */
1332
1333} /* [namespace I2n] */