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
40 #include <sys/timeb.h>
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)
66 ret=tb.time+(static_cast<float>(tb.millitm)/1000);
71 // converts ISO-DATE: 2003-06-13
72 time_t date_to_seconds(const std::string &date)
75 int year = -1, month = -1, day = -1;
77 string::size_type pos = date.find("-");
78 if (pos == string::npos)
81 istringstream in(string(date,0,pos));
85 string dstr(date, pos+1);
86 if ((pos = dstr.find("-")) == string::npos)
90 in.str(string(dstr, 0, pos));
95 in.str(string(dstr, pos+1));
98 if (year < 0 || month == -1 || day == -1)
102 memset(&tm_struct, 0, sizeof(struct tm));
103 tm_struct.tm_year = year;
104 tm_struct.tm_mon = month;
105 tm_struct.tm_mday = day;
106 tm_struct.tm_isdst = -1;
108 rtn = mktime (&tm_struct);
112 string make_nice_time(int seconds)
116 int days=seconds/86400;
120 split_daysec(seconds,&hours,&minutes,&seconds);
123 out << days << " " << i18n_plural("day", "days", days) << ", ";
126 out << setw(2) << hours << ':' << setw(2) << minutes << ':' << setw(2) << seconds;
131 string format_full_time(time_t seconds)
136 if (localtime_r((time_t *)&seconds, &ta) == NULL)
137 memset (&ta, 0, sizeof(struct tm));
139 strftime (buf, 49, "%d.%m.%Y %H:%M", &ta);
143 string format_date(time_t seconds)
148 if (localtime_r((time_t *)&seconds, &ta) == NULL)
149 memset (&ta, 0, sizeof(struct tm));
151 strftime (buf, 49, "%d.%m.%Y", &ta);
155 void seconds_to_hour_minute(int seconds, int *hour, int *minute)
159 while (seconds >= 3600) {
165 if (minute != NULL) {
167 while (seconds >= 60) {
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
181 void split_daysec(int daysec, int *outhours, int *outminutes, int *outseconds)
183 int hours=daysec/3600;
186 int minutes=daysec/60;
199 std::string output_hour_minute(int hour, int minute, bool h_for_00, int seconds)
203 if (hour >= 0 && hour < 10)
207 if (!h_for_00 || minute != 0 || seconds > 0)
210 if (minute >= 0 && minute < 10)
220 if (seconds > 0 && seconds < 10)
228 string get_month_name(unsigned char month)
233 rtn = i18n("January");
236 rtn = i18n("February");
254 rtn = i18n("August");
257 rtn = i18n("September");
260 rtn = i18n("October");
263 rtn = i18n("November");
266 rtn = i18n("December");
271 out << i18n("Illegal month:") << " " << month;
281 ** implementaion of Interval
286 * @brief clears the interval (make it empty).
288 void Interval::clear()
290 m_lower_bound = m_upper_bound = 0;
291 } // eo Interval::clear()
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.
299 bool Interval::intersects(const Interval& other) const
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 )
309 } // eo Interval::intersects(const Interval&)
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.
317 bool Interval::contains(const Interval& other) const
319 return (other.m_lower_bound >= m_lower_bound)
320 and (other.m_upper_bound <= m_upper_bound)
322 } // eo Interval::contains(const Interval& other) const
326 ** implementation of Intervals:
330 Intervals::Intervals()
332 } // eo Intervals::Intervals
335 void Intervals::clear()
338 } // eo Intervals::clear()
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.
345 bool Intervals::intersects(const Interval& other) const
347 for(const_iterator it= begin();
351 if ( it->intersects(other) )
357 } // eo Intervals::intersects(const Interval&) const
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.
365 bool Intervals::intersects(const Intervals& other) const
367 for(const_iterator it= begin();
371 if ( other.intersects( *it ) )
377 } // eo Intervals::intersects(const Intervals&) const
381 * @brief adds a new interval to the list.
382 * @param new_frame the new interval.
384 * Adds the interval to the list and joins overlapping intervals.
386 * @internal complexity O(n).
388 void Intervals::add(const Interval& new_frame)
390 if (not new_frame.is_valid() or new_frame.empty())
392 // well... we will not insert invalid or empty frames!
395 for (IntervalList::iterator it= m_intervals.begin();
396 it != m_intervals.end();
399 Interval& current_frame = *it;
400 if ( new_frame.m_lower_bound > current_frame.m_upper_bound )
402 // new_frame begins later than current end; go on:
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:
408 if ( new_frame.m_upper_bound < current_frame.m_lower_bound )
410 // new disjoint frame; insert it before the current frame:
411 m_intervals.insert( it, new_frame );
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 )
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)
421 // the new interval starts earlier; we need to adjust our current frame:
422 current_frame.m_lower_bound = new_frame.m_lower_bound;
423 current_frame.m_changed = true;
425 // NOTE no "else" part needed since in that case our current frame already
426 // contains the new one!
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*
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)
438 // yes, we need to move the start:
439 current_frame.m_lower_bound = new_frame.m_lower_bound;
440 current_frame.m_changed= true;
443 // now let's extend the end:
444 current_frame.m_upper_bound = new_frame.m_upper_bound;
445 current_frame.m_changed = true;
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
453 Interval next_frame= *it2;
454 if ( current_frame.m_upper_bound < next_frame.m_upper_bound )
456 // in this case our end is within the next frame.
458 current_frame.m_upper_bound = next_frame.m_upper_bound;
460 // and remove the next frame since the current frame contains it (now):
461 m_intervals.erase(it2);
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&)
473 * @brief subtracts a time interval from the list.
474 * @param del_frame the time interval to subtract.
476 * removes the time interval from the list; cut off parts from or remove existing
477 * intervals if they overlap.
479 * @internal complexity O(n).
481 void Intervals::sub(const Interval& del_frame)
483 if (not del_frame.is_valid() or del_frame.empty() )
487 for (IntervalList::iterator it= m_intervals.begin();
488 it != m_intervals.end();
491 Interval& current_frame = *it;
492 if ( del_frame.m_lower_bound >= current_frame.m_upper_bound )
494 // del_frame begins later than current end; go on:
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 )
501 // end is before our start; nothing to do.
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 )
507 // del frame end point is within our interval.
508 if ( del_frame.m_lower_bound > current_frame.m_lower_bound)
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 ) );
513 // adjust start of current frame:
514 if (current_frame.m_lower_bound < del_frame.m_upper_bound)
516 current_frame.m_lower_bound= del_frame.m_upper_bound;
517 current_frame.m_changed= true;
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 )
525 // a part of the current interval needs to be preserved..
527 current_frame.m_upper_bound= del_frame.m_lower_bound;
528 current_frame.m_changed= true;
529 // and continue with the next interval:
533 // at this point; the whole frame needs to be deleted..
534 if ( it == m_intervals.begin())
536 m_intervals.erase(it);
537 it= m_intervals.begin();
541 IntervalList::iterator it2= it++;
542 m_intervals.erase(it2);
545 } // eo Intervals::sub(const Interval&)
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.
553 bool Intervals::contains(const Interval& other) const
555 for(const_iterator it= begin();
559 if ( it->contains( other ))
565 } // eo Intervals::contains(const Interval&) const
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.
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.
576 bool Intervals::contains_exact(const Interval& other) const
578 for(const_iterator it= begin();
588 } // eo Intervals::contains_exact(const Interval&)const
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.
596 * @internal we rely on the fact that the lists are sorted and contain
597 * disjoint intervals.
599 * So this method has a complexity of O(n).
601 bool Intervals::contains(const Intervals& other) const
603 const_iterator my_it= begin();
604 const_iterator other_it= other.begin();
605 while( my_it != end() and other_it!= other.end() )
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
619 if (not my_it->contains( *other_it ))
621 // if we don't contain the current other; we're done:
624 //else check the next other interval:
627 return (other_it == other.end());
628 } // eo Intervals::contains(const Intervals&) const
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.
636 * @internal since the lists are sorted, we compare the interval lists.
637 * Thus we have a complexity of O(n).
639 bool Intervals::operator==(const Intervals& other) const
641 // since we keep sorted lists: just compare the lists :-)
642 return m_intervals == other.m_intervals;
643 } // eo Intervals::operator==(const Intervals&)
646 Intervals& Intervals::operator+=(const Interval& other)
650 } // eo operator+=(const Interval&)
653 Intervals& Intervals::operator-=(const Interval& other)
657 } // eo operator-=(const Interval&)
661 * @brief adds the intervals of a second instance to us.
662 * @param other the other instance.
663 * @return self reference (allow chaining).
665 * @internal since we do simple loops over the other and our intervals
666 * we have a complexity of O(n^2).
668 * @todo optimize if complexity becomes a problem.
670 Intervals& Intervals::operator+=(const Intervals& other)
672 for(const_iterator it= other.begin();
679 } // eo operator+=(const Intervals&)
683 * @brief subtracts the intervals of a second instance from us.
684 * @param other the other instance.
685 * @return self reference (allow chaining).
687 * @internal since we do simple loops over the other and our intervals
688 * we have a complexity of O(n^2).
690 * @todo optimize if complexity becomes a problem.
692 Intervals& Intervals::operator-=(const Intervals& other)
700 for(const_iterator it= other.begin();
708 } // eo operator-=(const Intervals&)
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.
723 bool monotonic_clock_gettime(long int& seconds, long int& nano_seconds)
725 struct timespec tp[1];
726 int res= clock_gettime (CLOCK_MONOTONIC, tp);
730 nano_seconds= tp->tv_nsec;
733 } // eo monotonic_clock_gettime(long int&,long int&)
737 * @brief fetches the value from the monotonic clock source.
738 * @return the time since system start in nanoseconds, 0 if read was unsuccessful
740 long long monotonic_clock_gettime_nano()
743 long int nano_seconds;
746 if (monotonic_clock_gettime(seconds,nano_seconds))
757 * @brief fetches the value from the monotonic clock source.
758 * @param[out] seconds the seconds.
759 * @param[out] nano_seconds the nano seconds.
760 * @return @a true if the clock was successfully read.
762 bool realtime_clock_gettime(long int& seconds, long int& nano_seconds)
764 struct timespec tp[1];
765 int res= clock_gettime(CLOCK_REALTIME, tp);
769 nano_seconds= tp->tv_nsec;
772 } // eo realtime_clock_gettime(long int&,long int&)
776 * There is a discrepancy of one input character
777 * due to the lack of sign handling in strptime(3):
779 * - strftime(3) needs the year specified as %5Y to account for the
781 * - strptime(3) will not parse the leading dash with that format
782 * but apart from that it works well.
788 * max: -YYYYYYYYYY-MM-DDThh:mm:ssZ+zzzz ⇒ 32
789 * That is assuming the year in broken down time is an int we
790 * need to reserve ten decimal places.
792 // static_assert (sizeof (((struct tm *)NULL)->tm_year) == 4);
793 static const size_t bufsize = 33;
805 * Unfortunately the glibc strptime(3) on the Intranator trips over
806 * the length specifier in field descriptors so we can’t reuse the
807 * formatters here. This problem is fixed in newer glibc. For the time
808 * being we keep two tables of formatters and choose the appropriate
812 static const char *const formatter [ISO8601_SIZE] =
813 { /* [iso8601::d ] = */ "%4Y-%m-%d",
814 /* [iso8601::t ] = */ "%T",
815 /* [iso8601::tz ] = */ "%TZ%z",
816 /* [iso8601::dt ] = */ "%4Y-%m-%dT%T",
817 /* [iso8601::dtz] = */ "%4Y-%m-%dT%TZ%z",
820 static const char *const scanner [ISO8601_SIZE] =
821 { /* [iso8601::d ] = */ "%Y-%m-%d",
822 /* [iso8601::t ] = */ "%T",
823 /* [iso8601::tz ] = */ "%TZ%z",
824 /* [iso8601::dt ] = */ "%Y-%m-%dT%T",
825 /* [iso8601::dtz] = */ "%Y-%m-%dT%TZ%z",
828 static inline const char *
829 pick_fmt (const bool date, const bool time, const bool tz, const bool scan=false)
831 const char *const *table = scan ? iso8601::scanner : iso8601::formatter;
832 enum iso8601::kind format = iso8601::dtz;
837 format = iso8601::dtz;
839 format = iso8601::dt;
844 } else if (time && tz) {
845 format = iso8601::tz;
847 format = iso8601::t; /* default to %T */
850 return table [format];
858 static inline int flip_tm_year (const int y)
859 { return (y + 1900) * -1 - 1900; }
863 * @brief Format a time structure according to ISO-8601, e. g.
864 * “2018-01-09T10:40:00Z+0100”; see \c strftime(3) for
867 * @param tm Time to format as broken-down \c struct tm.
868 * @param date Include the day part ([-]YYYY-MM-DD).
869 * @param time Include the time part (hh:mm:ss).
870 * @param tz Include the timezone ([±]ZZZZ); only heeded if
871 * \c time is requested as well.
873 * @return The formatted timestamp.
875 std::string format_iso8601 (const struct tm &tm, const bool date,
876 const bool time, const bool tz)
879 char buf [iso8601::bufsize] = { 0 };
880 char *start = &buf [0];
881 const char *format = iso8601::pick_fmt (date, time, tz);
883 memcpy (&tmp, &tm, sizeof (tmp));
885 if (tmp.tm_year < -1900) { /* negative year */
888 tmp.tm_year = flip_tm_year (tmp.tm_year);
892 * The sign is *always* handled above so the formatted string her
893 * is always one character shorter.
895 const size_t n = strftime (start, iso8601::bufsize-1, format, &tmp);
899 return std::string (buf);
902 typedef struct tm * (*time_breakdown_fn) (const time_t *, struct tm *);
905 * @brief Format a UNIX timestamp according to ISO-8601. Converts
906 * to broken down time first.
908 * @param t Time to format as broken-down \c struct tm.
909 * @param date Include the day part ([-]YYYY-MM-DD).
910 * @param time Include the time part (hh:mm:ss).
911 * @param tz Include the timezone ([±]ZZZZ); only heeded if
912 * \c time is requested as well.
914 * @return The formatted timestamp.
916 std::string format_iso8601 (time_t t, const bool utc, const bool date,
917 const bool time, const bool tz)
919 time_breakdown_fn breakdown = utc ? gmtime_r : localtime_r;
923 if (breakdown (&t, &tm) == NULL) {
924 return std::string ("error analyzing timestamp: ") + strerror (errno);
927 return format_iso8601 (tm, date, time, tz);
931 * @brief Read a ISO-8601 formatted date stamp into broken down time.
933 * @param s String containing the timestamp.
935 * @return \c boost::none if the input string was \c NULL or malformed,
936 * an optional \c struct tm with the extracted values otherwise.
938 boost::optional<struct tm>
939 scan_iso8601 (const char *s,
940 const bool date, const bool time, const bool tz) NOEXCEPT
943 const char *format = iso8601::pick_fmt (date, time, tz, true);
944 const char *start = s;
945 bool negyear = false;
957 * Contrary to what the man page indicates, strptime(3) is *not*
958 * the inverse operation of strftime(3)! The later correctly formats
959 * negative year numbers with the %F modifier wheres the former trips
960 * over the sign character.
972 memset (&tm, 0, sizeof (tm));
974 if (strptime (start, format, &tm) == NULL) {
979 tm.tm_year = flip_tm_year (tm.tm_year);
992 static inline clockid_t
993 clockid_of_flags (const enum type::id id,
994 const enum type::variant var) NOEXCEPT
996 clockid_t cid = CLOCK_MONOTONIC_COARSE;
1007 cid = CLOCK_MONOTONIC_RAW;
1011 cid = CLOCK_MONOTONIC;
1019 if (var == type::exact) {
1020 cid = CLOCK_REALTIME;
1022 cid = CLOCK_REALTIME_COARSE;
1028 if (var & type::exact) {
1029 cid = CLOCK_BOOTTIME;
1035 if (var == type::thread) {
1036 cid = CLOCK_THREAD_CPUTIME_ID;
1038 cid = CLOCK_PROCESS_CPUTIME_ID;
1047 static const struct timespec zero_time = { 0, 0 };
1051 Time::Time (const enum type::id id,
1052 const enum type::variant var) NOEXCEPT
1059 Time::Time (const struct tm &tm,
1060 const enum type::id id,
1061 const enum type::variant var) NOEXCEPT
1063 struct tm tmp_tm; /* dummy for mktime(3) */
1066 memcpy (&tmp_tm, &tm, sizeof (tmp_tm));
1067 tmp_time = Time (mktime (&tmp_tm), 0l, id, var);
1069 this->swap (tmp_time);
1073 Time::as_nanosec (void) const NOEXCEPT
1075 return int64_t (this->value.tv_sec) * TIME_CONST_FACTOR_NANO
1076 + this->value.tv_nsec;
1080 Time::as_nanosec_L (void) const NOEXCEPT /* likely to overflow */
1081 { return static_cast<long>(this->as_nanosec ()); }
1084 Time::operator= (Time t2) NOEXCEPT
1092 Time::operator= (struct timespec ts) NOEXCEPT
1094 std::swap (this->value, ts);
1095 this->id = clock::type::mono;
1096 this->variant = clock::type::dflt;
1103 Time::unset (void) NOEXCEPT
1104 { this->value = zero_time; }
1107 Time::set (void) NOEXCEPT
1109 struct timespec now;
1112 if (clock_gettime (clockid_of_flags (this->id, this->variant), &now)
1127 Time::add (const time_t sec, const long nsec) NOEXCEPT
1129 this->value.tv_sec += sec;
1130 this->value.tv_nsec += nsec;
1132 this->carry_nsec ();
1138 Time::subtract (const time_t sec, const long nsec) NOEXCEPT
1140 this->value.tv_sec -= sec;
1141 this->value.tv_nsec -= nsec;
1143 this->carry_nsec ();
1149 Time::scale (const int64_t factor) NOEXCEPT
1151 this->value.tv_sec *= factor;
1152 this->value.tv_nsec *= factor;
1154 this->carry_nsec ();
1160 * Below division code purposely does not attempt to handle divide-
1161 * by-zero just as any other C++ division function does. It is up to
1162 * the caller to ensure that the divisor is not zero.
1165 Time::divide (const int64_t divisor) NOEXCEPT
1167 const long sec = static_cast<long> (this->value.tv_sec );
1168 int64_t nsec = static_cast<int64_t> (this->value.tv_nsec);
1169 const ldiv_t div = ldiv (sec, divisor);
1172 nsec += div.rem * TIME_CONST_FACTOR_NANO;
1177 this->value.tv_sec = static_cast<time_t> (div.quot);
1178 this->value.tv_nsec = static_cast<long> (nsec);
1180 this->carry_nsec ();
1185 boost::optional<std::string>
1186 Time::format_iso8601 (const bool utc,
1189 const bool tz) const
1191 time_breakdown_fn breakdown = utc ? gmtime_r : localtime_r;
1194 if (breakdown (&this->value.tv_sec, &tm) == NULL) {
1198 return ::format_iso8601 (tm, date, time, tz);
1201 boost::optional<std::string>
1202 Time::make_nice_time (void) const
1204 /* XXX the cast below results in loss of precision with 64 bit time_t! */
1205 return ::make_nice_time (static_cast<int> (this->value.tv_sec));
1208 boost::optional<std::string>
1209 Time::format_full_time (void) const
1210 { return ::format_full_time (this->value.tv_sec); }
1212 boost::optional<std::string>
1213 Time::format_date (void) const
1214 { return ::format_date (this->value.tv_sec); }
1216 boost::optional<Time>
1217 now (const enum type::id id, const enum type::variant var) NOEXCEPT
1229 zero (const enum type::id id, const enum type::variant var) NOEXCEPT
1230 { return Time (id, var); }
1233 compare (const Time &t1, const Time &t2) NOEXCEPT
1235 if (t1.value.tv_sec < t2.value.tv_sec) {
1239 if (t1.value.tv_sec > t2.value.tv_sec) {
1243 if (t1.value.tv_nsec < t2.value.tv_nsec) {
1247 if (t1.value.tv_nsec > t2.value.tv_nsec) {
1254 boost::optional<Time>
1255 time_of_iso8601 (const std::string &s,
1259 const enum type::id id,
1260 const enum type::variant var) NOEXCEPT
1262 boost::optional<struct tm> tm = scan_iso8601 (s, date, time, tz);
1268 return Time (*tm, id, var);
1271 } /* [namespace clock] */
1273 } /* [namespace I2n] */