2 The software in this package is distributed under the GNU General
3 Public License version 2 (with a special exception described below).
5 A copy of GNU General Public License (GPL) is included in this distribution,
6 in the file COPYING.GPL.
8 As a special exception, if other files instantiate templates or use macros
9 or inline functions from this file, or you compile this file and link it
10 with other works to produce a work based on this file, this file
11 does not by itself cause the resulting work to be covered
12 by the GNU General Public License.
14 However the source code for this file must still be made available
15 in accordance with section (3) of the GNU General Public License.
17 This exception does not invalidate any other reasons why a work based
18 on this file might be covered by the GNU General Public License.
21 * @brief time related functions.
23 * @copyright Copyright © 2001-2008 by Intra2net AG
42 #include <timefunc.hxx>
46 // define missing POSIX.1b constants...
48 #ifndef CLOCK_REALTIME
49 #define CLOCK_REALTIME 0
51 #ifndef CLOCK_MONOTONIC
52 #define CLOCK_MONOTONIC 1
59 double prec_time(void)
61 struct timespec ts = { 0 };
64 if (clock_gettime(CLOCK_REALTIME_COARSE, &ts) == -1) {
65 /* Something’s wrong on the kernel end! */
69 ret = static_cast<double>(ts.tv_sec);
70 ret += static_cast<double>(ts.tv_nsec)
71 / static_cast<double>(TIME_CONST_FACTOR_NANO);
76 // converts ISO-DATE: 2003-06-13
77 time_t date_to_seconds(const std::string &date)
80 int year = -1, month = -1, day = -1;
82 string::size_type pos = date.find("-");
83 if (pos == string::npos)
86 istringstream in(string(date,0,pos));
90 string dstr(date, pos+1);
91 if ((pos = dstr.find("-")) == string::npos)
95 in.str(string(dstr, 0, pos));
100 in.str(string(dstr, pos+1));
103 if (year < 0 || month == -1 || day == -1)
107 memset(&tm_struct, 0, sizeof(struct tm));
108 tm_struct.tm_year = year;
109 tm_struct.tm_mon = month;
110 tm_struct.tm_mday = day;
111 tm_struct.tm_isdst = -1;
113 rtn = mktime (&tm_struct);
117 string make_nice_time(int seconds)
121 int days=seconds/86400;
125 split_daysec(seconds,&hours,&minutes,&seconds);
128 out << days << " " << i18n_plural("day", "days", days) << ", ";
131 out << setw(2) << hours << ':' << setw(2) << minutes << ':' << setw(2) << seconds;
136 string format_full_time(time_t seconds)
141 if (localtime_r((time_t *)&seconds, &ta) == NULL)
142 memset (&ta, 0, sizeof(struct tm));
144 strftime (buf, 49, "%d.%m.%Y %H:%M", &ta);
148 string format_date(time_t seconds)
153 if (localtime_r((time_t *)&seconds, &ta) == NULL)
154 memset (&ta, 0, sizeof(struct tm));
156 strftime (buf, 49, "%d.%m.%Y", &ta);
160 void seconds_to_hour_minute(int seconds, int *hour, int *minute)
164 while (seconds >= 3600) {
170 if (minute != NULL) {
172 while (seconds >= 60) {
180 * Split seconds into hours, minutes and seconds
181 * @param [in] daysec Seconds since start of day
182 * @param [out] outhours hours
183 * @param [out] outminutes minutes
184 * @param [out] outseconds seconds
186 void split_daysec(int daysec, int *outhours, int *outminutes, int *outseconds)
188 int hours=daysec/3600;
191 int minutes=daysec/60;
204 std::string output_hour_minute(int hour, int minute, bool h_for_00, int seconds)
208 if (hour >= 0 && hour < 10)
212 if (!h_for_00 || minute != 0 || seconds > 0)
215 if (minute >= 0 && minute < 10)
225 if (seconds > 0 && seconds < 10)
233 string get_month_name(unsigned char month)
238 rtn = i18n("January");
241 rtn = i18n("February");
259 rtn = i18n("August");
262 rtn = i18n("September");
265 rtn = i18n("October");
268 rtn = i18n("November");
271 rtn = i18n("December");
276 out << i18n("Illegal month:") << " " << month;
286 ** implementaion of Interval
291 * @brief clears the interval (make it empty).
293 void Interval::clear()
295 m_lower_bound = m_upper_bound = 0;
296 } // eo Interval::clear()
300 * @brief tests if there is some overlapping with another interval
301 * @param other the other interval
302 * @return @a true if the two intervals have a non empty intersection.
304 bool Interval::intersects(const Interval& other) const
307 // // other start within this:
308 (other.m_lower_bound >= m_lower_bound and other.m_lower_bound < m_upper_bound )
309 // // other end within this:
310 or (other.m_upper_bound > m_lower_bound and other.m_upper_bound <= m_upper_bound )
311 // // other contains this
312 or (other.m_lower_bound <= m_lower_bound and other.m_upper_bound >= m_upper_bound )
314 } // eo Interval::intersects(const Interval&)
318 * @brief tests if the current interval (fully) contains another one.
319 * @param other the other interval.
320 * @return @a true if the current interval fully contains the other interval.
322 bool Interval::contains(const Interval& other) const
324 return (other.m_lower_bound >= m_lower_bound)
325 and (other.m_upper_bound <= m_upper_bound)
327 } // eo Interval::contains(const Interval& other) const
331 ** implementation of Intervals:
335 Intervals::Intervals()
337 } // eo Intervals::Intervals
340 void Intervals::clear()
343 } // eo Intervals::clear()
346 * @brief tests if one of the intervals of the list intersects with the given interval.
347 * @param other the interval to check for intersection.
348 * @return @a true if there is an intersection.
350 bool Intervals::intersects(const Interval& other) const
352 for(const_iterator it= begin();
356 if ( it->intersects(other) )
362 } // eo Intervals::intersects(const Interval&) const
366 * @brief tests if we have at least one intersection with another Intervals instance.
367 * @param other the other instance.
368 * @return @a true if there is an intersection.
370 bool Intervals::intersects(const Intervals& other) const
372 for(const_iterator it= begin();
376 if ( other.intersects( *it ) )
382 } // eo Intervals::intersects(const Intervals&) const
386 * @brief adds a new interval to the list.
387 * @param new_frame the new interval.
389 * Adds the interval to the list and joins overlapping intervals.
391 * @internal complexity O(n).
393 void Intervals::add(const Interval& new_frame)
395 if (not new_frame.is_valid() or new_frame.empty())
397 // well... we will not insert invalid or empty frames!
400 for (IntervalList::iterator it= m_intervals.begin();
401 it != m_intervals.end();
404 Interval& current_frame = *it;
405 if ( new_frame.m_lower_bound > current_frame.m_upper_bound )
407 // new_frame begins later than current end; go on:
410 // at this point: the begin of the new frame is less then the current end.
411 // now let's determine how we can insert the new frame:
413 if ( new_frame.m_upper_bound < current_frame.m_lower_bound )
415 // new disjoint frame; insert it before the current frame:
416 m_intervals.insert( it, new_frame );
420 // at this point: the end of the new frame is >= current begin.
421 if ( new_frame.m_upper_bound <= current_frame.m_upper_bound )
423 // the end of the new frame is within our current frame; we need to combine
424 if (new_frame.m_lower_bound < current_frame.m_lower_bound)
426 // the new interval starts earlier; we need to adjust our current frame:
427 current_frame.m_lower_bound = new_frame.m_lower_bound;
428 current_frame.m_changed = true;
430 // NOTE no "else" part needed since in that case our current frame already
431 // contains the new one!
436 // at this point: end of new frame > end of current frame
437 // so we need to extend the current frame; at least the end.
438 // But we need to deal with intersects of following frames... *sigh*
440 // first the simple part: let's see if we need to move the start:
441 if ( new_frame.m_lower_bound < current_frame.m_lower_bound)
443 // yes, we need to move the start:
444 current_frame.m_lower_bound = new_frame.m_lower_bound;
445 current_frame.m_changed= true;
448 // now let's extend the end:
449 current_frame.m_upper_bound = new_frame.m_upper_bound;
450 current_frame.m_changed = true;
452 // well... let's walk through the following frames; looking for more joins...:
453 IntervalList::iterator it2 = it;
454 while( ++(it2=it) != m_intervals.end()
455 and current_frame.m_upper_bound >= it2->m_lower_bound
458 Interval next_frame= *it2;
459 if ( current_frame.m_upper_bound < next_frame.m_upper_bound )
461 // in this case our end is within the next frame.
463 current_frame.m_upper_bound = next_frame.m_upper_bound;
465 // and remove the next frame since the current frame contains it (now):
466 m_intervals.erase(it2);
471 // at this point: new frame starts later than the last frame ends
472 // append the new frame:
473 m_intervals.push_back( new_frame );
474 } // eo Intervals::add(const Interval&)
478 * @brief subtracts a time interval from the list.
479 * @param del_frame the time interval to subtract.
481 * removes the time interval from the list; cut off parts from or remove existing
482 * intervals if they overlap.
484 * @internal complexity O(n).
486 void Intervals::sub(const Interval& del_frame)
488 if (not del_frame.is_valid() or del_frame.empty() )
492 for (IntervalList::iterator it= m_intervals.begin();
493 it != m_intervals.end();
496 Interval& current_frame = *it;
497 if ( del_frame.m_lower_bound >= current_frame.m_upper_bound )
499 // del_frame begins later than current end; go on:
503 // at this point: the begin of the del frame is less then the current end.
504 if ( del_frame.m_upper_bound < current_frame.m_lower_bound )
506 // end is before our start; nothing to do.
509 // at this point: the end of the del frame is >= current begin.
510 if ( del_frame.m_upper_bound < current_frame.m_upper_bound )
512 // del frame end point is within our interval.
513 if ( del_frame.m_lower_bound > current_frame.m_lower_bound)
515 // the del frame is within our interval... we need to split:
516 m_intervals.insert(it, Interval( current_frame.m_lower_bound, del_frame.m_lower_bound ) );
518 // adjust start of current frame:
519 if (current_frame.m_lower_bound < del_frame.m_upper_bound)
521 current_frame.m_lower_bound= del_frame.m_upper_bound;
522 current_frame.m_changed= true;
527 // at this point the end of the del frame is >= current end
528 if ( del_frame.m_lower_bound > current_frame.m_lower_bound )
530 // a part of the current interval needs to be preserved..
532 current_frame.m_upper_bound= del_frame.m_lower_bound;
533 current_frame.m_changed= true;
534 // and continue with the next interval:
538 // at this point; the whole frame needs to be deleted..
539 if ( it == m_intervals.begin())
541 m_intervals.erase(it);
542 it= m_intervals.begin();
546 IntervalList::iterator it2= it++;
547 m_intervals.erase(it2);
550 } // eo Intervals::sub(const Interval&)
554 * @brief returns if we contain an interval.
555 * @param other the interval to check.
556 * @return @a true if we cover the given interval, too.
558 bool Intervals::contains(const Interval& other) const
560 for(const_iterator it= begin();
564 if ( it->contains( other ))
570 } // eo Intervals::contains(const Interval&) const
574 * @brief returns if we contain an exact interval.
575 * @param other the interval to check.
576 * @return @a true if we axactly contains the given interval.
578 * @note thsi differs from contain in the way, that we return only @a true
579 * iff we have the given interval in our list; not only cover it.
581 bool Intervals::contains_exact(const Interval& other) const
583 for(const_iterator it= begin();
593 } // eo Intervals::contains_exact(const Interval&)const
597 * @brief returns if we contain another interval combination.
598 * @param other the intervals to check.
599 * @return @a true if we cover the given intervals, too.
601 * @internal we rely on the fact that the lists are sorted and contain
602 * disjoint intervals.
604 * So this method has a complexity of O(n).
606 bool Intervals::contains(const Intervals& other) const
608 const_iterator my_it= begin();
609 const_iterator other_it= other.begin();
610 while( my_it != end() and other_it!= other.end() )
612 // seek the first interval which contains the lower bound of the current other interval
613 while (my_it != end()
614 and my_it->m_lower_bound > other_it->m_lower_bound
615 and other_it->m_lower_bound >= my_it->m_upper_bound
624 if (not my_it->contains( *other_it ))
626 // if we don't contain the current other; we're done:
629 //else check the next other interval:
632 return (other_it == other.end());
633 } // eo Intervals::contains(const Intervals&) const
637 * @brief combines to interval combinates for equality
638 * @param other the other instance.
639 * @return @a true if the other is equal to the current.
641 * @internal since the lists are sorted, we compare the interval lists.
642 * Thus we have a complexity of O(n).
644 bool Intervals::operator==(const Intervals& other) const
646 // since we keep sorted lists: just compare the lists :-)
647 return m_intervals == other.m_intervals;
648 } // eo Intervals::operator==(const Intervals&)
651 Intervals& Intervals::operator+=(const Interval& other)
655 } // eo operator+=(const Interval&)
658 Intervals& Intervals::operator-=(const Interval& other)
662 } // eo operator-=(const Interval&)
666 * @brief adds the intervals of a second instance to us.
667 * @param other the other instance.
668 * @return self reference (allow chaining).
670 * @internal since we do simple loops over the other and our intervals
671 * we have a complexity of O(n^2).
673 * @todo optimize if complexity becomes a problem.
675 Intervals& Intervals::operator+=(const Intervals& other)
677 for(const_iterator it= other.begin();
684 } // eo operator+=(const Intervals&)
688 * @brief subtracts the intervals of a second instance from us.
689 * @param other the other instance.
690 * @return self reference (allow chaining).
692 * @internal since we do simple loops over the other and our intervals
693 * we have a complexity of O(n^2).
695 * @todo optimize if complexity becomes a problem.
697 Intervals& Intervals::operator-=(const Intervals& other)
705 for(const_iterator it= other.begin();
713 } // eo operator-=(const Intervals&)
723 * @brief fetches the value from the monotonic clock source.
724 * @param[out] seconds the seconds.
725 * @param[out] nano_seconds the nano seconds.
726 * @return @a true if the clock was successfully read.
728 bool monotonic_clock_gettime(long int& seconds, long int& nano_seconds)
730 struct timespec tp[1];
731 int res= clock_gettime (CLOCK_MONOTONIC, tp);
735 nano_seconds= tp->tv_nsec;
738 } // eo monotonic_clock_gettime(long int&,long int&)
742 * @brief fetches the value from the monotonic clock source.
743 * @return the time since system start in nanoseconds, 0 if read was unsuccessful
745 long long monotonic_clock_gettime_nano()
748 long int nano_seconds;
751 if (monotonic_clock_gettime(seconds,nano_seconds))
762 * @brief fetches the value from the monotonic clock source.
763 * @param[out] seconds the seconds.
764 * @param[out] nano_seconds the nano seconds.
765 * @return @a true if the clock was successfully read.
767 bool realtime_clock_gettime(long int& seconds, long int& nano_seconds)
769 struct timespec tp[1];
770 int res= clock_gettime(CLOCK_REALTIME, tp);
774 nano_seconds= tp->tv_nsec;
777 } // eo realtime_clock_gettime(long int&,long int&)
781 * There is a discrepancy of one input character
782 * due to the lack of sign handling in strptime(3):
784 * - strftime(3) needs the year specified as %5Y to account for the
786 * - strptime(3) will not parse the leading dash with that format
787 * but apart from that it works well.
793 * max: -YYYYYYYYYY-MM-DDThh:mm:ssZ+zzzz ⇒ 32
794 * That is assuming the year in broken down time is an int we
795 * need to reserve ten decimal places.
797 // static_assert (sizeof (((struct tm *)NULL)->tm_year) == 4);
798 static const size_t bufsize = 33;
810 * Unfortunately the glibc strptime(3) on the Intranator trips over
811 * the length specifier in field descriptors so we can’t reuse the
812 * formatters here. This problem is fixed in newer glibc. For the time
813 * being we keep two tables of formatters and choose the appropriate
817 static const char *const formatter [ISO8601_SIZE] =
818 { /* [iso8601::d ] = */ "%4Y-%m-%d",
819 /* [iso8601::t ] = */ "%T",
820 /* [iso8601::tz ] = */ "%TZ%z",
821 /* [iso8601::dt ] = */ "%4Y-%m-%dT%T",
822 /* [iso8601::dtz] = */ "%4Y-%m-%dT%TZ%z",
825 static const char *const scanner [ISO8601_SIZE] =
826 { /* [iso8601::d ] = */ "%Y-%m-%d",
827 /* [iso8601::t ] = */ "%T",
828 /* [iso8601::tz ] = */ "%TZ%z",
829 /* [iso8601::dt ] = */ "%Y-%m-%dT%T",
830 /* [iso8601::dtz] = */ "%Y-%m-%dT%TZ%z",
833 static inline const char *
834 pick_fmt (const bool date, const bool time, const bool tz, const bool scan=false)
836 const char *const *table = scan ? iso8601::scanner : iso8601::formatter;
837 enum iso8601::kind format = iso8601::dtz;
842 format = iso8601::dtz;
844 format = iso8601::dt;
849 } else if (time && tz) {
850 format = iso8601::tz;
852 format = iso8601::t; /* default to %T */
855 return table [format];
863 static inline int flip_tm_year (const int y)
864 { return (y + 1900) * -1 - 1900; }
868 * @brief Format a time structure according to ISO-8601, e. g.
869 * “2018-01-09T10:40:00Z+0100”; see \c strftime(3) for
872 * @param tm Time to format as broken-down \c struct tm.
873 * @param date Include the day part ([-]YYYY-MM-DD).
874 * @param time Include the time part (hh:mm:ss).
875 * @param tz Include the timezone ([±]ZZZZ); only needed if
876 * \c time is requested as well.
878 * @return The formatted timestamp.
880 std::string format_iso8601 (const struct tm &tm, const bool date,
881 const bool time, const bool tz)
884 char buf [iso8601::bufsize] = { 0 };
885 char *start = &buf [0];
886 const char *format = iso8601::pick_fmt (date, time, tz);
888 memcpy (&tmp, &tm, sizeof (tmp));
890 if (tmp.tm_year < -1900) { /* negative year */
893 tmp.tm_year = flip_tm_year (tmp.tm_year);
897 * The sign is *always* handled above so the formatted string here
898 * is always one character shorter.
900 if (strftime (start, iso8601::bufsize-1, format, &tmp) == 0)
902 return std::string ();
905 buf [iso8601::bufsize-1] = '\0'; /* Just in case. */
907 return std::string (buf);
910 typedef struct tm * (*time_breakdown_fn) (const time_t *, struct tm *);
913 * @brief Format a UNIX timestamp according to ISO-8601. Converts
914 * to broken down time first.
916 * @param t Time to format as broken-down \c struct tm.
917 * @param date Include the day part ([-]YYYY-MM-DD).
918 * @param time Include the time part (hh:mm:ss).
919 * @param tz Include the timezone ([±]ZZZZ); only heeded if
920 * \c time is requested as well.
922 * @return The formatted timestamp.
924 std::string format_iso8601 (time_t t, const bool utc, const bool date,
925 const bool time, const bool tz)
927 time_breakdown_fn breakdown = utc ? gmtime_r : localtime_r;
931 if (breakdown (&t, &tm) == NULL) {
932 return std::string ("error analyzing timestamp: ") + strerror (errno);
935 return format_iso8601 (tm, date, time, tz);
939 * @brief Read a ISO-8601 formatted date stamp into broken down time.
941 * @param s String containing the timestamp.
943 * @return \c boost::none if the input string was \c NULL or malformed,
944 * an optional \c struct tm with the extracted values otherwise.
946 boost::optional<struct tm>
947 scan_iso8601 (const char *s,
948 const bool date, const bool time, const bool tz) NOEXCEPT
951 const char *format = iso8601::pick_fmt (date, time, tz, true);
952 const char *start = s;
953 bool negyear = false;
965 * Contrary to what the man page indicates, strptime(3) is *not*
966 * the inverse operation of strftime(3)! The later correctly formats
967 * negative year numbers with the %F modifier wheres the former trips
968 * over the sign character.
980 memset (&tm, 0, sizeof (tm));
982 if (strptime (start, format, &tm) == NULL) {
987 tm.tm_year = flip_tm_year (tm.tm_year);
994 * @brief Format a \c struct timespec in the schema established by
995 * time(1): “3m14.159s”.
997 * @param ts The time spec to format.
999 * @return \c boost:none in case of error during formatting, an optional
1000 * \c std::string otherwise.
1002 boost::optional<std::string>
1003 format_min_sec_msec (const struct timespec &ts)
1005 char ms [4] = { '\0', '\0', '\0', '\0' };
1007 if (snprintf (ms, 4, "%.3ld", ts.tv_nsec / 1000000) < 0) {
1011 const time_t min = ts.tv_sec / 60;
1012 const time_t sec = ts.tv_sec - min * 60;
1014 return I2n::to_string (min) + "m"
1015 + I2n::to_string (sec) + "."
1027 * @brief <b>For internal use only</b>. Translates clock
1028 * specification flags to kernel clock types.
1030 * @param id Master clock id: \c mono, \c real, \c boot, or \c
1032 * @param var Variant of clock if appropriate: \c raw, \c exact, \c
1033 * process, or \c thread. Use \c dflt for the base
1036 * @return The clock id for using with kernel APIs.
1038 static inline clockid_t
1039 clockid_of_flags (const enum type::id id,
1040 const enum type::variant var) NOEXCEPT
1042 clockid_t cid = CLOCK_MONOTONIC_COARSE;
1053 cid = CLOCK_MONOTONIC_RAW;
1057 cid = CLOCK_MONOTONIC;
1065 if (var == type::exact) {
1066 cid = CLOCK_REALTIME;
1068 cid = CLOCK_REALTIME_COARSE;
1074 cid = CLOCK_BOOTTIME;
1079 if (var == type::thread) {
1080 cid = CLOCK_THREAD_CPUTIME_ID;
1082 cid = CLOCK_PROCESS_CPUTIME_ID;
1091 static const struct timespec zero_time = { 0, 0 };
1095 Time::Time (const enum type::id id,
1096 const enum type::variant var) NOEXCEPT
1104 * Ctor from *struct tm*. On 32 bit systems the conversion to *time_t* will
1105 * fail with years outside the range from epoch to 2038.
1107 Time::Time (const struct tm &tm,
1108 const enum type::id id,
1109 const enum type::variant var)
1111 struct tm tmp_tm; /* dummy for mktime(3) */
1114 memcpy (&tmp_tm, &tm, sizeof (tmp_tm));
1117 const time_t t = mktime (&tmp_tm);
1118 if (t == - 1) { /* Glibc does not set errno on out-of-range here! */
1119 const char *datestr = asctime (&tm);
1120 throw conversion_error (errno,
1121 std::string ("mktime: from struct tm {")
1122 + std::string (datestr, 0, strlen(datestr)-1)
1126 tmp_time = Time (t, 0l, id, var);
1128 this->swap (tmp_time);
1132 Time::as_nanosec (void) const NOEXCEPT
1134 return int64_t (this->value.tv_sec) * TIME_CONST_FACTOR_NANO
1135 + this->value.tv_nsec;
1139 Time::as_nanosec_L (void) const NOEXCEPT /* likely to overflow */
1140 { return static_cast<long>(this->as_nanosec ()); }
1143 Time::operator= (Time t2) NOEXCEPT
1151 * @note This operator is an up-assignment from a type containing less
1152 * information than the structure assigned from. Since the
1153 * operator can only be two valued we must normalize the remaining
1154 * fields to the default clock. When assigning from non-default
1155 * clocks, use the appropriate constructor and pass it the desired
1156 * id and variant so as to assign the result.
1159 Time::operator= (struct timespec ts) NOEXCEPT
1161 std::swap (this->value, ts);
1162 this->id = clock::type::mono;
1163 this->variant = clock::type::dflt;
1170 Time::unset (void) NOEXCEPT
1171 { this->value = zero_time; }
1174 Time::set (void) NOEXCEPT
1176 struct timespec now;
1179 if (clock_gettime (clockid_of_flags (this->id, this->variant), &now)
1194 Time::add (const time_t sec, const long nsec) NOEXCEPT
1196 this->value.tv_sec += sec;
1197 this->value.tv_nsec += nsec;
1199 this->carry_nsec ();
1205 Time::subtract (const time_t sec, const long nsec) NOEXCEPT
1207 this->value.tv_sec -= sec;
1208 this->value.tv_nsec -= nsec;
1210 this->carry_nsec ();
1216 Time::scale (const int64_t factor) NOEXCEPT
1218 this->value.tv_sec *= factor;
1219 this->value.tv_nsec *= factor;
1221 this->carry_nsec ();
1227 * Below division code purposely does not attempt to handle divide-
1228 * by-zero just as any other C++ division function does. It is up to
1229 * the caller to ensure that the divisor is not zero.
1232 Time::divide (const int64_t divisor) NOEXCEPT
1234 const long sec = static_cast<long> (this->value.tv_sec );
1235 int64_t nsec = static_cast<int64_t> (this->value.tv_nsec);
1236 const ldiv_t div = ldiv (sec, divisor);
1239 nsec += div.rem * TIME_CONST_FACTOR_NANO;
1244 this->value.tv_sec = static_cast<time_t> (div.quot);
1245 this->value.tv_nsec = static_cast<long> (nsec);
1247 this->carry_nsec ();
1253 * @brief Format timestamp according to the ISO standard rules.
1255 * @param utc Whether to normalize the timestamp to UTC or local time.
1256 * @param date Whether to include the date (%F).
1257 * @param time Whether to include the time (%T).
1258 * @param tz Whether to include the UTC offset (%z).
1260 * @return \c none if the formatting operation failed, the
1261 * formatted timestamp otherwise.
1263 * @note The standard allows for extending the format using
1264 * a fractional component. However, this is subject to
1265 * local conventions so we don’t support it. For more
1266 * than seconds granularity use a better suited format
1267 * like LDAP Generalized time instead.
1269 boost::optional<std::string>
1270 Time::format_iso8601 (const bool utc,
1273 const bool tz) const
1275 time_breakdown_fn breakdown = utc ? gmtime_r : localtime_r;
1278 if (breakdown (&this->value.tv_sec, &tm) == NULL) {
1282 return ::format_iso8601 (tm, date, time, tz);
1286 Time::make_nice_time (void) const
1288 /* XXX the cast below results in loss of precision with 64 bit time_t! */
1289 return ::make_nice_time (static_cast<int> (this->value.tv_sec));
1293 Time::format_full_time (void) const
1294 { return ::format_full_time (this->value.tv_sec); }
1297 Time::format_date (void) const
1298 { return ::format_date (this->value.tv_sec); }
1301 * @brief Obtain the current time wrt. the given
1304 * @param id Clock id.
1305 * @param var Clock variant.
1307 * @return \c none if the underlying \c clock_gettime() operation
1308 * failed, a fully initialized \c struct Time otherwise.
1310 boost::optional<Time>
1311 now (const enum type::id id, const enum type::variant var) NOEXCEPT
1323 zero (const enum type::id id, const enum type::variant var) NOEXCEPT
1324 { return Time (id, var); }
1327 * @brief Standard three-way comparison for \c struct Time
1328 * relying on strict total ordering.
1330 * @param t1 Comparand.
1331 * @param t2 Comparand.
1333 * @return -1, 0, 1 depending on whether t1 is less-than, equal,
1334 * or greater than t2.
1336 * @note This should be used to implement the spaceship operator
1337 * (P0515R0) when we get a new compiler.
1340 compare (const Time &t1, const Time &t2) NOEXCEPT
1342 if (t1.value.tv_sec < t2.value.tv_sec) {
1346 if (t1.value.tv_sec > t2.value.tv_sec) {
1350 if (t1.value.tv_nsec < t2.value.tv_nsec) {
1354 if (t1.value.tv_nsec > t2.value.tv_nsec) {
1362 * @brief Interpret string as timestamp according to the ISO
1365 * This is the inverse operation of \c format_iso8601().
1367 * @param s Input string to read. The entire string is interpreted
1368 * and it must not contain any trailing data.
1369 * @param date Whether to parse the date (%F).
1370 * @param time Whether to parse the time (%T).
1371 * @param tz Whether to parse the UTC offset (%z).
1372 * @param id Clock id to assign the result.
1373 * @param var Clock variant to assign the result.
1375 * @return \c none if the input could not be parsed according to
1376 * ISO rules, a \c struct Time otherwise.
1378 boost::optional<Time>
1379 time_of_iso8601 (const std::string &s,
1383 const enum type::id id,
1384 const enum type::variant var) NOEXCEPT
1386 boost::optional<struct tm> tm = scan_iso8601 (s, date, time, tz);
1393 return Time (*tm, id, var);
1395 catch (conversion_error &_unused) { }
1400 } /* [namespace clock] */
1402 } /* [namespace I2n] */