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