extend Time class with string formatters
[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
d70f7269 27#include <errno.h>
e93545dd
GE
28#include <string>
29#include <sstream>
30#include <iostream>
31#include <iomanip>
f1499910
GE
32#include <bitset>
33#include <stdexcept>
1b5dfd98
TJ
34#include <iterator>
35#include <algorithm>
e93545dd
GE
36
37#include <time.h>
96d0be2e 38#include <unistd.h>
5efd35b1 39#include <string.h>
e93545dd
GE
40#include <sys/timeb.h>
41
42#include <timefunc.hxx>
43#include <i18n.h>
44
96d0be2e
TJ
45
46// define missing POSIX.1b constants...
47
48#ifndef CLOCK_REALTIME
49#define CLOCK_REALTIME 0
50#endif
51#ifndef CLOCK_MONOTONIC
52#define CLOCK_MONOTONIC 1
53#endif
54
55
56
e93545dd
GE
57using namespace std;
58
59double prec_time(void)
60{
61 struct timeb tb;
62 double ret;
63
64 ftime(&tb);
65
66 ret=tb.time+(static_cast<float>(tb.millitm)/1000);
67
68 return ret;
69}
70
71// converts ISO-DATE: 2003-06-13
dad9e26f 72time_t date_to_seconds(const std::string &date)
e93545dd 73{
dad9e26f
GE
74 time_t rtn = 0;
75 int year = -1, month = -1, day = -1;
e93545dd
GE
76
77 string::size_type pos = date.find("-");
78 if (pos == string::npos)
79 return rtn;
80
81 istringstream in(string(date,0,pos));
82 in >> year;
83 year -= 1900;
84
85 string dstr(date, pos+1);
86 if ((pos = dstr.find("-")) == string::npos)
87 return rtn;
88
89 in.clear();
90 in.str(string(dstr, 0, pos));
91 in >> month;
92 month -= 1;
93
94 in.clear();
95 in.str(string(dstr, pos+1));
96 in >> day;
97
98 if (year < 0 || month == -1 || day == -1)
99 return rtn;
4cf3676b 100
e93545dd 101 struct tm tm_struct;
4cf3676b 102 memset(&tm_struct, 0, sizeof(struct tm));
e93545dd
GE
103 tm_struct.tm_year = year;
104 tm_struct.tm_mon = month;
105 tm_struct.tm_mday = day;
106 tm_struct.tm_isdst = -1;
107
108 rtn = mktime (&tm_struct);
109 return rtn;
110}
111
112string make_nice_time(int seconds)
113{
114 ostringstream out;
115
116 int days=seconds/86400;
117 seconds%=86400;
118
c0368918
GE
119 int hours,minutes;
120 split_daysec(seconds,&hours,&minutes,&seconds);
121
c7bd7a65
CH
122 if (days>0)
123 out << days << " " << i18n_plural("day", "days", days) << ", ";
e93545dd
GE
124
125 out << setfill('0');
126 out << setw(2) << hours << ':' << setw(2) << minutes << ':' << setw(2) << seconds;
127
128 return out.str();
129}
130
7839bd53 131string format_full_time(time_t seconds)
e93545dd
GE
132{
133 char buf[50];
134 memset (buf, 0, 50);
532d6b3a
TJ
135 struct tm ta;
136 if (localtime_r((time_t *)&seconds, &ta) == NULL)
137 memset (&ta, 0, sizeof(struct tm));
e93545dd 138
532d6b3a 139 strftime (buf, 49, "%d.%m.%Y %H:%M", &ta);
e93545dd
GE
140 return string(buf);
141}
f1499910 142
25f3d405
CH
143string format_date(time_t seconds)
144{
145 char buf[50];
146 memset (buf, 0, 50);
147 struct tm ta;
148 if (localtime_r((time_t *)&seconds, &ta) == NULL)
149 memset (&ta, 0, sizeof(struct tm));
150
151 strftime (buf, 49, "%d.%m.%Y", &ta);
152 return string(buf);
153}
154
87869870
GE
155void seconds_to_hour_minute(int seconds, int *hour, int *minute)
156{
157 if (hour != NULL) {
158 *hour = 0;
159 while (seconds >= 3600) {
160 seconds-=3600;
161 (*hour)++;
162 }
163 }
164
165 if (minute != NULL) {
166 *minute = 0;
167 while (seconds >= 60) {
168 seconds-=60;
169 (*minute)++;
170 }
171 }
172}
173
c0368918
GE
174/**
175 * Split seconds into hours, minutes and seconds
176 * @param [in] daysec Seconds since start of day
177 * @param [out] outhours hours
178 * @param [out] outminutes minutes
179 * @param [out] outseconds seconds
180 */
181void split_daysec(int daysec, int *outhours, int *outminutes, int *outseconds)
182{
183 int hours=daysec/3600;
184 daysec%=3600;
185
186 int minutes=daysec/60;
187 daysec%=60;
188
189 if (outhours)
190 *outhours=hours;
191
192 if (outminutes)
193 *outminutes=minutes;
194
195 if (outseconds)
196 *outseconds=daysec;
197}
198
199std::string output_hour_minute(int hour, int minute, bool h_for_00, int seconds)
2c66f490
GE
200{
201 ostringstream out;
202
203 if (hour >= 0 && hour < 10)
204 out << '0';
205 out << hour;
206
c0368918 207 if (!h_for_00 || minute != 0 || seconds > 0)
2c66f490
GE
208 {
209 out << ':';
fe223928 210 if (minute >= 0 && minute < 10)
2c66f490
GE
211 out << '0';
212 out << minute;
213 }
214 else
215 out << 'h';
216
c0368918
GE
217 if (seconds > 0)
218 {
219 out << ':';
220 if (seconds > 0 && seconds < 10)
221 out << '0';
222 out << seconds;
223 }
224
2c66f490
GE
225 return out.str();
226}
4e157d1d
TJ
227
228string get_month_name(unsigned char month)
229{
230 string rtn;
231 switch(month) {
232 case 1:
233 rtn = i18n("January");
234 break;
235 case 2:
236 rtn = i18n("February");
237 break;
238 case 3:
239 rtn = i18n("March");
240 break;
241 case 4:
242 rtn = i18n("April");
243 break;
244 case 5:
245 rtn = i18n("May");
246 break;
247 case 6:
248 rtn = i18n("June");
249 break;
250 case 7:
251 rtn = i18n("July");
252 break;
253 case 8:
254 rtn = i18n("August");
255 break;
256 case 9:
257 rtn = i18n("September");
258 break;
259 case 10:
260 rtn = i18n("October");
261 break;
262 case 11:
263 rtn = i18n("November");
264 break;
265 case 12:
266 rtn = i18n("December");
267 break;
268 default:
269 {
270 ostringstream out;
271 out << i18n("Illegal month:") << " " << month;
272 rtn = out.str();
273 }
274 }
275
276 return rtn;
277}
1b5dfd98
TJ
278
279
280/*
281** implementaion of Interval
282*/
283
d181c3bc
TJ
284
285/**
286 * @brief clears the interval (make it empty).
287 */
288void Interval::clear()
289{
290 m_lower_bound = m_upper_bound = 0;
291} // eo Interval::clear()
292
293
1b5dfd98
TJ
294/**
295 * @brief tests if there is some overlapping with another interval
296 * @param other the other interval
297 * @return @a true if the two intervals have a non empty intersection.
298 */
299bool Interval::intersects(const Interval& other) const
300{
301 return
302 // // other start within this:
303 (other.m_lower_bound >= m_lower_bound and other.m_lower_bound < m_upper_bound )
304 // // other end within this:
305 or (other.m_upper_bound > m_lower_bound and other.m_upper_bound <= m_upper_bound )
306 // // other contains this
307 or (other.m_lower_bound <= m_lower_bound and other.m_upper_bound >= m_upper_bound )
308 ;
309} // eo Interval::intersects(const Interval&)
310
311
312/**
313 * @brief tests if the current interval (fully) contains another one.
314 * @param other the other interval.
315 * @return @a true if the current interval fully contains the other interval.
316 */
317bool Interval::contains(const Interval& other) const
318{
319 return (other.m_lower_bound >= m_lower_bound)
320 and (other.m_upper_bound <= m_upper_bound)
321 ;
322} // eo Interval::contains(const Interval& other) const
323
324
325/*
326** implementation of Intervals:
327*/
328
329
330Intervals::Intervals()
331{
332} // eo Intervals::Intervals
333
334
d181c3bc
TJ
335void Intervals::clear()
336{
337 m_intervals.clear();
338} // eo Intervals::clear()
339
1b5dfd98
TJ
340/**
341 * @brief tests if one of the intervals of the list intersects with the given interval.
342 * @param other the interval to check for intersection.
343 * @return @a true if there is an intersection.
344 */
345bool Intervals::intersects(const Interval& other) const
346{
347 for(const_iterator it= begin();
348 it != end();
349 ++it)
350 {
351 if ( it->intersects(other) )
352 {
353 return true;
354 }
355 }
356 return false;
357} // eo Intervals::intersects(const Interval&) const
358
359
360/**
361 * @brief tests if we have at least one intersection with another Intervals instance.
362 * @param other the other instance.
363 * @return @a true if there is an intersection.
364 */
365bool Intervals::intersects(const Intervals& other) const
366{
367 for(const_iterator it= begin();
368 it != end();
369 ++it)
370 {
371 if ( other.intersects( *it ) )
372 {
373 return true;
374 }
375 }
376 return false;
377} // eo Intervals::intersects(const Intervals&) const
378
379
380/**
381 * @brief adds a new interval to the list.
382 * @param new_frame the new interval.
383 *
384 * Adds the interval to the list and joins overlapping intervals.
ebc3b584
TJ
385 *
386 * @internal complexity O(n).
1b5dfd98
TJ
387 */
388void Intervals::add(const Interval& new_frame)
389{
390 if (not new_frame.is_valid() or new_frame.empty())
391 {
392 // well... we will not insert invalid or empty frames!
393 return;
394 }
395 for (IntervalList::iterator it= m_intervals.begin();
396 it != m_intervals.end();
397 ++it)
398 {
399 Interval& current_frame = *it;
400 if ( new_frame.m_lower_bound > current_frame.m_upper_bound )
401 {
402 // new_frame begins later than current end; go on:
403 continue;
404 }
405 // at this point: the begin of the new frame is less then the current end.
406 // now let's determine how we can insert the new frame:
407
408 if ( new_frame.m_upper_bound < current_frame.m_lower_bound )
409 {
410 // new disjoint frame; insert it before the current frame:
411 m_intervals.insert( it, new_frame );
412 // and we are done.
413 return;
414 }
415 // at this point: the end of the new frame is >= current begin.
416 if ( new_frame.m_upper_bound <= current_frame.m_upper_bound )
417 {
418 // the end of the new frame is within our current frame; we need to combine
419 if (new_frame.m_lower_bound < current_frame.m_lower_bound)
420 {
421 // the new interval starts earlier; we need to adjust our current frame:
422 current_frame.m_lower_bound = new_frame.m_lower_bound;
80f30818 423 current_frame.m_changed = true;
1b5dfd98
TJ
424 }
425 // NOTE no "else" part needed since in that case our current frame already
426 // contains the new one!
427
428 // we are done:
429 return;
430 }
431 // at this point: end of new frame > end of current frame
432 // so we need to extend the current frame; at least the end.
433 // But we need to deal with intersects of following frames... *sigh*
434
435 // first the simple part: let's see if we need to move the start:
436 if ( new_frame.m_lower_bound < current_frame.m_lower_bound)
437 {
438 // yes, we need to move the start:
439 current_frame.m_lower_bound = new_frame.m_lower_bound;
80f30818 440 current_frame.m_changed= true;
1b5dfd98
TJ
441 }
442
443 // now let's extend the end:
444 current_frame.m_upper_bound = new_frame.m_upper_bound;
80f30818 445 current_frame.m_changed = true;
1b5dfd98
TJ
446
447 // well... let's walk through the following frames; looking for more joins...:
448 IntervalList::iterator it2 = it;
449 while( ++(it2=it) != m_intervals.end()
450 and current_frame.m_upper_bound >= it2->m_lower_bound
451 )
452 {
453 Interval next_frame= *it2;
454 if ( current_frame.m_upper_bound < next_frame.m_upper_bound )
455 {
456 // in this case our end is within the next frame.
457 // adjust our end.
458 current_frame.m_upper_bound = next_frame.m_upper_bound;
459 }
460 // and remove the next frame since the current frame contains it (now):
461 m_intervals.erase(it2);
462 }
463 // we are done!
464 return;
465 }
466 // at this point: new frame starts later than the last frame ends
467 // append the new frame:
468 m_intervals.push_back( new_frame );
469} // eo Intervals::add(const Interval&)
470
471
472/**
473 * @brief subtracts a time interval from the list.
474 * @param del_frame the time interval to subtract.
475 *
476 * removes the time interval from the list; cut off parts from or remove existing
477 * intervals if they overlap.
ebc3b584
TJ
478 *
479 * @internal complexity O(n).
1b5dfd98
TJ
480 */
481void Intervals::sub(const Interval& del_frame)
482{
483 if (not del_frame.is_valid() or del_frame.empty() )
484 {
485 return;
486 }
487 for (IntervalList::iterator it= m_intervals.begin();
488 it != m_intervals.end();
489 )
490 {
491 Interval& current_frame = *it;
492 if ( del_frame.m_lower_bound >= current_frame.m_upper_bound )
493 {
494 // del_frame begins later than current end; go on:
495 ++it;
496 continue;
497 }
498 // at this point: the begin of the del frame is less then the current end.
499 if ( del_frame.m_upper_bound < current_frame.m_lower_bound )
500 {
501 // end is before our start; nothing to do.
502 return;
503 }
504 // at this point: the end of the del frame is >= current begin.
505 if ( del_frame.m_upper_bound < current_frame.m_upper_bound )
506 {
507 // del frame end point is within our interval.
508 if ( del_frame.m_lower_bound > current_frame.m_lower_bound)
509 {
510 // the del frame is within our interval... we need to split:
511 m_intervals.insert(it, Interval( current_frame.m_lower_bound, del_frame.m_lower_bound ) );
512 }
513 // adjust start of current frame:
80f30818
TJ
514 if (current_frame.m_lower_bound < del_frame.m_upper_bound)
515 {
516 current_frame.m_lower_bound= del_frame.m_upper_bound;
517 current_frame.m_changed= true;
518 }
1b5dfd98
TJ
519 // and we are done!
520 return;
521 }
522 // at this point the end of the del frame is >= current end
523 if ( del_frame.m_lower_bound > current_frame.m_lower_bound )
524 {
525 // a part of the current interval needs to be preserved..
526 // move the end.
527 current_frame.m_upper_bound= del_frame.m_lower_bound;
80f30818 528 current_frame.m_changed= true;
1b5dfd98
TJ
529 // and continue with the next interval:
530 ++it;
531 continue;
532 }
533 // at this point; the whole frame needs to be deleted..
534 if ( it == m_intervals.begin())
535 {
536 m_intervals.erase(it);
537 it= m_intervals.begin();
538 }
539 else
540 {
541 IntervalList::iterator it2= it++;
542 m_intervals.erase(it2);
543 }
544 }
545} // eo Intervals::sub(const Interval&)
546
547
548/**
549 * @brief returns if we contain an interval.
550 * @param other the interval to check.
551 * @return @a true if we cover the given interval, too.
552 */
553bool Intervals::contains(const Interval& other) const
554{
555 for(const_iterator it= begin();
556 it != end();
557 ++it)
558 {
559 if ( it->contains( other ))
560 {
561 return true;
562 }
563 }
564 return false;
565} // eo Intervals::contains(const Interval&) const
566
567
568/**
e156de7c
TJ
569 * @brief returns if we contain an exact interval.
570 * @param other the interval to check.
571 * @return @a true if we axactly contains the given interval.
572 *
573 * @note thsi differs from contain in the way, that we return only @a true
574 * iff we have the given interval in our list; not only cover it.
575 */
576bool Intervals::contains_exact(const Interval& other) const
577{
578 for(const_iterator it= begin();
579 it != end();
580 ++it)
581 {
582 if ( *it == other)
583 {
584 return true;
585 }
586 }
587 return false;
588} // eo Intervals::contains_exact(const Interval&)const
589
590
591/**
1b5dfd98
TJ
592 * @brief returns if we contain another interval combination.
593 * @param other the intervals to check.
594 * @return @a true if we cover the given intervals, too.
595 *
596 * @internal we rely on the fact that the lists are sorted and contain
597 * disjoint intervals.
ebc3b584
TJ
598 *
599 * So this method has a complexity of O(n).
1b5dfd98
TJ
600 */
601bool Intervals::contains(const Intervals& other) const
602{
603 const_iterator my_it= begin();
604 const_iterator other_it= other.begin();
605 while( my_it != end() and other_it!= other.end() )
606 {
607 // seek the first interval which contains the lower bound of the current other interval
608 while (my_it != end()
609 and my_it->m_lower_bound > other_it->m_lower_bound
610 and other_it->m_lower_bound >= my_it->m_upper_bound
611 )
612 {
613 ++my_it;
614 }
615 if (my_it == end())
616 {
617 break;
618 }
619 if (not my_it->contains( *other_it ))
620 {
621 // if we don't contain the current other; we're done:
622 return false;
623 }
624 //else check the next other interval:
625 ++other_it;
626 }
627 return (other_it == other.end());
ebc3b584 628} // eo Intervals::contains(const Intervals&) const
1b5dfd98
TJ
629
630
ebc3b584
TJ
631/**
632 * @brief combines to interval combinates for equality
633 * @param other the other instance.
634 * @return @a true if the other is equal to the current.
635 *
636 * @internal since the lists are sorted, we compare the interval lists.
637 * Thus we have a complexity of O(n).
638 */
1b5dfd98
TJ
639bool Intervals::operator==(const Intervals& other) const
640{
641 // since we keep sorted lists: just compare the lists :-)
642 return m_intervals == other.m_intervals;
643} // eo Intervals::operator==(const Intervals&)
644
645
646Intervals& Intervals::operator+=(const Interval& other)
647{
648 add(other);
649 return *this;
650} // eo operator+=(const Interval&)
651
652
653Intervals& Intervals::operator-=(const Interval& other)
654{
655 sub(other);
656 return *this;
657} // eo operator-=(const Interval&)
658
659
ebc3b584
TJ
660/**
661 * @brief adds the intervals of a second instance to us.
662 * @param other the other instance.
663 * @return self reference (allow chaining).
664 *
665 * @internal since we do simple loops over the other and our intervals
666 * we have a complexity of O(n^2).
667 *
668 * @todo optimize if complexity becomes a problem.
669 */
1b5dfd98
TJ
670Intervals& Intervals::operator+=(const Intervals& other)
671{
672 for(const_iterator it= other.begin();
673 it != other.end();
674 ++it)
675 {
676 add( *it );
677 }
678 return *this;
679} // eo operator+=(const Intervals&)
680
681
ebc3b584
TJ
682/**
683 * @brief subtracts the intervals of a second instance from us.
684 * @param other the other instance.
685 * @return self reference (allow chaining).
686 *
687 * @internal since we do simple loops over the other and our intervals
688 * we have a complexity of O(n^2).
689 *
690 * @todo optimize if complexity becomes a problem.
691 */
1b5dfd98
TJ
692Intervals& Intervals::operator-=(const Intervals& other)
693{
694 if (&other == this)
695 {
696 m_intervals.clear();
697 }
698 else
699 {
700 for(const_iterator it= other.begin();
701 it != other.end();
702 ++it)
703 {
704 sub( *it );
705 }
706 }
707 return *this;
708} // eo operator-=(const Intervals&)
96d0be2e
TJ
709
710
711
712/*
713** clock funcs:
714*/
715
716
717/**
718 * @brief fetches the value from the monotonic clock source.
719 * @param[out] seconds the seconds.
720 * @param[out] nano_seconds the nano seconds.
721 * @return @a true if the clock was successfully read.
722 */
723bool monotonic_clock_gettime(long int& seconds, long int& nano_seconds)
724{
725 struct timespec tp[1];
4b1afed1 726 int res= clock_gettime (CLOCK_MONOTONIC, tp);
96d0be2e
TJ
727 if (0 == res)
728 {
729 seconds= tp->tv_sec;
730 nano_seconds= tp->tv_nsec;
731 }
732 return (res==0);
733} // eo monotonic_clock_gettime(long int&,long int&)
734
735
736/**
737 * @brief fetches the value from the monotonic clock source.
b7e17426
GE
738 * @return the time since system start in nanoseconds, 0 if read was unsuccessful
739 */
740long long monotonic_clock_gettime_nano()
741{
742 long int seconds;
743 long int nano_seconds;
744 long long nano=0;
745
746 if (monotonic_clock_gettime(seconds,nano_seconds))
747 {
748 nano=seconds;
749 nano*=1000000000LL;
750 nano+=nano_seconds;
751 }
752
753 return nano;
754}
755
756/**
757 * @brief fetches the value from the monotonic clock source.
96d0be2e
TJ
758 * @param[out] seconds the seconds.
759 * @param[out] nano_seconds the nano seconds.
760 * @return @a true if the clock was successfully read.
761 */
762bool realtime_clock_gettime(long int& seconds, long int& nano_seconds)
763{
764 struct timespec tp[1];
4b1afed1 765 int res= clock_gettime(CLOCK_REALTIME, tp);
96d0be2e
TJ
766 if (0 == res)
767 {
768 seconds= tp->tv_sec;
769 nano_seconds= tp->tv_nsec;
770 }
771 return (res==0);
772} // eo realtime_clock_gettime(long int&,long int&)
773
774
d70f7269
PG
775static const char *const iso8601_fmt_d = "%F";
776static const char *const iso8601_fmt_t = "%T";
777static const char *const iso8601_fmt_tz = "%TZ%z";
778static const char *const iso8601_fmt_dt = "%FT%T";
779static const char *const iso8601_fmt_dtz = "%FT%TZ%z";
780
781/**
782 * @brief Format a time structure according to ISO-8601, e. g.
783 * “2018-01-09T10:40:00Z+0100”; see \c strftime(3) for
784 * the details.
785 *
786 * @param tm Time to format as broken-down \c struct tm.
787 * @param date Include the day part ([-]YYYY-MM-DD).
788 * @param time Include the time part (hh:mm:ss).
789 * @param tz Include the timezone ([±]ZZZZ); only heeded if
790 * \c time is requested as well.
791 *
792 * @return The formatted timestamp.
793 */
794std::string format_iso8601 (const struct tm &tm, const bool date,
795 const bool time, const bool tz)
796{
797# define ISO8601_BUFSIZE 27 /* max: -YYYY-MM-DDThh:mm:ssZ+zzzz ⇒ 26 */
798 char buf [ISO8601_BUFSIZE] = { 0 };
799 const char *format = NULL;
800
801 if (date) {
802 if (time) {
803 if (tz) {
804 format = iso8601_fmt_dtz;
805 } else {
806 format = iso8601_fmt_dt;
807 }
808 } else {
809 format = iso8601_fmt_d;
810 }
811 } else if (time && tz) {
812 format = iso8601_fmt_tz;
813 } else {
814 format = iso8601_fmt_t; /* default to %T */
815 }
816
817 const size_t n = strftime (buf, ISO8601_BUFSIZE, format, &tm);
818
819 buf [n] = '\0';
820
821 return std::string (buf);
822}
823
824
825typedef struct tm * (*time_breakdown_fn) (const time_t *, struct tm *);
826
827/**
828 * @brief Format a UNIX timestamp according to ISO-8601. Converts
829 * to broken down time first.
830 *
831 * @param t Time to format as broken-down \c struct tm.
832 * @param date Include the day part ([-]YYYY-MM-DD).
833 * @param time Include the time part (hh:mm:ss).
834 * @param tz Include the timezone ([±]ZZZZ); only heeded if
835 * \c time is requested as well.
836 *
837 * @return The formatted timestamp.
838 */
839std::string format_iso8601 (time_t t, const bool utc, const bool date,
840 const bool time, const bool tz)
841{
842 time_breakdown_fn breakdown = utc ? gmtime_r : localtime_r;
843 struct tm tm;
844
845 errno = 0;
846 if (breakdown (&t, &tm) == NULL) {
847 return std::string ("error analyzing timestamp: ") + strerror (errno);
848 }
849
850 return format_iso8601 (tm, date, time, tz);
851}
852
853
8b5814e2
PG
854namespace I2n {
855
856namespace clock {
857
858 namespace {
859
860 static inline clockid_t
861 clockid_of_flags (const enum type::id id,
e36ca33c 862 const enum type::variant var) NOEXCEPT
8b5814e2
PG
863 {
864 clockid_t cid = CLOCK_MONOTONIC_COARSE;
865
866 switch (id) {
867
868 default:
869 case type::mono: {
870 switch (var) {
871 default: {
872 break;
873 }
874 case type::raw: {
875 cid = CLOCK_MONOTONIC_RAW;
876 break;
877 }
878 case type::exact: {
879 cid = CLOCK_MONOTONIC;
880 break;
881 }
882 }
883 break;
884 }
885
886 case type::real: {
887 if (var == type::exact) {
888 cid = CLOCK_REALTIME;
889 } else {
890 cid = CLOCK_REALTIME_COARSE;
891 }
892 break;
893 }
894
895 case type::boot: {
896 if (var & type::exact) {
897 cid = CLOCK_BOOTTIME;
898 }
899 break;
900 }
901
902 case type::cpu: {
903 if (var == type::thread) {
904 cid = CLOCK_THREAD_CPUTIME_ID;
905 } else {
906 cid = CLOCK_PROCESS_CPUTIME_ID;
907 }
908 break;
909 }
910 } /* [switch id] */
911
912 return cid;
913 }
914
915 static const struct timespec zero_time = { 0, 0 };
916
8b5814e2
PG
917 } /* [namespace] */
918
e36ca33c
PG
919 Time::Time (const enum type::id id,
920 const enum type::variant var) NOEXCEPT
8b5814e2
PG
921 : value (zero_time)
922 , id (id)
923 , variant (var)
924 , err (0)
925 { }
926
927 int64_t
e36ca33c
PG
928 Time::as_nanosec (void) const NOEXCEPT
929 {
930 return int64_t (this->value.tv_sec) * TIME_CONST_FACTOR_NANO
931 + this->value.tv_nsec;
932 }
8b5814e2
PG
933
934 long
e36ca33c 935 Time::as_nanosec_L (void) const NOEXCEPT /* likely to overflow */
8b5814e2
PG
936 { return static_cast<long>(this->as_nanosec ()); }
937
e36ca33c
PG
938 Time &
939 Time::operator= (Time t2) NOEXCEPT
940 {
941 this->swap (t2);
942
943 return *this;
944 }
945
946 Time &
947 Time::operator= (struct timespec ts) NOEXCEPT
948 {
949 std::swap (this->value, ts);
950 this->id = clock::type::mono;
951 this->variant = clock::type::dflt;
952 this->err = 0;
953
954 return *this;
955 }
956
8b5814e2 957 void
e36ca33c 958 Time::unset (void) NOEXCEPT
8b5814e2
PG
959 { this->value = zero_time; }
960
961 bool
e36ca33c 962 Time::set (void) NOEXCEPT
8b5814e2
PG
963 {
964 struct timespec now;
965
966 errno = 0;
967 if (clock_gettime (clockid_of_flags (this->id, this->variant), &now)
968 == -1)
969 {
970 this->err = errno;
971 this->unset ();
972
973 return false;
974 }
975 this->err = 0;
976 this->value = now;
977
978 return true;
979 }
980
e36ca33c
PG
981 Time &
982 Time::add (const time_t sec, const long nsec) NOEXCEPT
983 {
984 this->value.tv_sec += sec;
985 this->value.tv_nsec += nsec;
986
987 this->carry_nsec ();
988
989 return *this;
990 }
991
992 Time &
993 Time::subtract (const time_t sec, const long nsec) NOEXCEPT
994 {
995 this->value.tv_sec -= sec;
996 this->value.tv_nsec -= nsec;
997
998 this->carry_nsec ();
999
1000 return *this;
1001 }
1002
1003 Time &
1004 Time::scale (const time_t factor) NOEXCEPT
1005 {
1006 this->value.tv_sec *= factor;
1007 this->value.tv_nsec *= factor;
1008
1009 this->carry_nsec ();
1010
1011 return *this;
1012 }
1013
72acd54c
PG
1014 boost::optional<std::string>
1015 Time::format_iso8601 (const bool utc,
1016 const bool date,
1017 const bool time,
1018 const bool tz) const
1019 {
1020 time_breakdown_fn breakdown = utc ? gmtime_r : localtime_r;
1021 struct tm tm;
1022
1023 if (breakdown (&this->value.tv_sec, &tm) == NULL) {
1024 return boost::none;
1025 }
1026
1027 return ::format_iso8601 (tm, date, time, tz);
1028 }
1029
1030 boost::optional<std::string>
1031 Time::make_nice_time (void) const
1032 {
1033 /* XXX the cast below results in loss of precision with 64 bit time_t! */
1034 return ::make_nice_time (static_cast<int> (this->value.tv_sec));
1035 }
1036
1037 boost::optional<std::string>
1038 Time::format_full_time (void) const
1039 { return ::format_full_time (this->value.tv_sec); }
1040
1041 boost::optional<std::string>
1042 Time::format_date (void) const
1043 { return ::format_date (this->value.tv_sec); }
1044
8b5814e2 1045 boost::optional<Time>
e36ca33c 1046 now (const enum type::id id, const enum type::variant var) NOEXCEPT
8b5814e2 1047 {
e36ca33c 1048 Time ret (id, var);
8b5814e2
PG
1049
1050 if (!ret.set ()) {
1051 return boost::none;
1052 }
1053
1054 return ret;
1055 }
1056
e36ca33c
PG
1057 Time
1058 zero (const enum type::id id, const enum type::variant var) NOEXCEPT
1059 { return Time (id, var); }
1060
1061 int
1062 compare (const Time &t1, const Time &t2) NOEXCEPT
1063 {
1064 if (t1.value.tv_sec < t2.value.tv_sec) {
1065 return -1;
1066 }
1067
1068 if (t1.value.tv_sec > t2.value.tv_sec) {
1069 return 1;
1070 }
1071
1072 if (t1.value.tv_nsec < t2.value.tv_nsec) {
1073 return -1;
1074 }
1075
1076 if (t1.value.tv_nsec > t2.value.tv_nsec) {
1077 return 1;
1078 }
1079
1080 return 0;
1081 }
1082
8b5814e2
PG
1083} /* [namespace clock] */
1084
1085} /* [namespace I2n] */