/* The software in this package is distributed under the GNU General Public License version 2 (with a special exception described below). A copy of GNU General Public License (GPL) is included in this distribution, in the file COPYING.GPL. As a special exception, if other files instantiate templates or use macros or inline functions from this file, or you compile this file and link it with other works to produce a work based on this file, this file does not by itself cause the resulting work to be covered by the GNU General Public License. However the source code for this file must still be made available in accordance with section (3) of the GNU General Public License. This exception does not invalidate any other reasons why a work based on this file might be covered by the GNU General Public License. */ /** @file * @brief time related functions. * * @copyright Copyright © 2001-2018 by Intra2net AG */ #ifndef __TIMEFUNC_HXX #define __TIMEFUNC_HXX #include #include #include #include #include #include #include #include "stringfunc.hxx" #if __cplusplus >= 201103 # define CONSTEXPR constexpr # define NOEXCEPT noexcept #else # define CONSTEXPR # define NOEXCEPT #endif #define TIME_CONST_FACTOR_NANO (1000L * 1000 * 1000) double prec_time(void); time_t date_to_seconds(const std::string &date); std::string make_nice_time(int seconds); std::string format_full_time(time_t seconds); std::string format_date(time_t seconds); std::string format_iso8601(const struct tm &tm, const bool date=true, const bool time=true, const bool tz=true); std::string format_iso8601(time_t t, const bool utc=true, const bool date=true, const bool time=true, const bool tz=true); inline std::string format_iso8601(const struct timespec &ts, const bool utc=true, const bool date=true, const bool time=true, const bool tz=true) { return format_iso8601 (ts.tv_sec, utc, date, time, tz); } inline std::string format_sec_msec (const struct timespec &ts) { return I2n::to_string (ts.tv_sec) + "s " + I2n::to_string (ts.tv_nsec / 1000000) + "ms" ; } boost::optional format_min_sec_msec (const struct timespec &ts); boost::optional scan_iso8601 (const char *s, const bool date=true, const bool time=true, const bool tz=true) NOEXCEPT; inline boost::optional scan_iso8601 (const std::string &s, const bool date=true, const bool time=true, const bool tz=true) NOEXCEPT { return scan_iso8601 (s.c_str (), date, time, tz); } void seconds_to_hour_minute(int seconds, int *hour, int *minute); void split_daysec(int daysec, int *outhours=NULL, int *outminutes=NULL, int *outseconds=NULL); std::string output_hour_minute(int hour, int minute, bool h_for_00=true, int seconds=0); inline std::string output_hour_minute_from_seconds(int seconds) { int hour, minute; split_daysec(seconds,&hour,&minute); return output_hour_minute(hour,minute); } std::string get_month_name(unsigned char month); /** * @brief structure representing a single (half-open) interval. */ class Interval { public: Interval() : m_lower_bound(0) , m_upper_bound(0) , m_weak_mark(0) , m_changed(false) { } // Interval( unsigned int start, unsigned int end, int weak_mark= 0 ) : m_lower_bound(start) , m_upper_bound(end) , m_weak_mark(weak_mark) , m_changed(false) { } // void clear(); bool is_valid() const { return m_lower_bound <= m_upper_bound; } // eo is_valid() const bool empty() const { return m_lower_bound == m_upper_bound; } // eo empty() const unsigned int lower_bound() const { return m_lower_bound; } unsigned int upper_bound() const { return m_upper_bound; } int weak_mark() const { return m_weak_mark; } bool changed() const { return m_changed; } bool operator== (const Interval& other) const { return m_lower_bound == other.m_lower_bound and m_upper_bound == other.m_upper_bound; } // eo operator==(const Interval&) bool operator!=(const Interval& other) const { return not (*this == other); } // eo operator!=(const Interval&) /** * @brief less operator. compares only the start times! * @param other the other interval to compare with. * @return @a true if the current start is less than the other start. */ bool operator<(const Interval& other) const { return m_lower_bound < other.m_lower_bound; } // eo operator<(const Interval&) bool intersects(const Interval& other) const; bool contains(const Interval& other) const; protected: friend class Intervals; unsigned int m_lower_bound; unsigned int m_upper_bound; int m_weak_mark; bool m_changed; }; // eo Interval /** * @brief structure representing a combination of single disjoint (half open) intervals. * * Basic idea is that this structure provides an interface for adding and * subtracting single intervals and keeps the internal list of intervals as * a list of disjoint intervals. * * @note the list is sorted by the start; lower comes first. * * @note the class provides some methods similar to STL container classes; * i.e. it can be used with (at least some) STL algorithms. * * @internal * we use a std::list for the intervals; this means we don't invalidate all * iterators when we insert or delete within loops... * And we use that fact!! */ class Intervals { public: typedef std::list< Interval > IntervalList; typedef IntervalList::value_type value_type; typedef IntervalList::const_iterator const_iterator; typedef IntervalList::const_iterator iterator; // we allow only const... typedef IntervalList::size_type size_type; public: Intervals(); void add(const Interval& new_frame); void sub(const Interval& new_frame); void clear(); const_iterator begin() const { return m_intervals.begin(); } const_iterator end() const { return m_intervals.end(); } bool empty() const { return m_intervals.empty(); } const Interval& front() const { return m_intervals.front(); } const Interval& back() const { return m_intervals.back(); } size_type size() const { return m_intervals.size(); } bool intersects(const Interval& other) const; bool intersects(const Intervals& other) const; bool contains(const Interval& other) const; bool contains(const Intervals& other) const; bool contains_exact(const Interval& other) const; bool operator==(const Intervals& other) const; bool operator!=(const Intervals& other) const { return not (*this == other) ; } Intervals& operator+=(const Interval& other); Intervals& operator-=(const Interval& other); Intervals& operator+=(const Intervals& other); Intervals& operator-=(const Intervals& other); protected: std::list< Interval > m_intervals; }; // eo Intervals /* ** clock funcs: */ bool monotonic_clock_gettime(long int& seconds, long int& nano_seconds); long long monotonic_clock_gettime_nano(); bool realtime_clock_gettime(long int& seconds, long int& nano_seconds); namespace I2n { namespace clock { class conversion_error : public std::exception { public: const int err; const std::string context; conversion_error (const int err, const std::string &context) : err (err) , context (context) { } virtual ~conversion_error (void) throw() { }; operator std::string (void) { return std::string ("errno=") + I2n::to_string (this->err) + " [" + this->context + "]"; } }; namespace { /* helper for ctor initializer list; we still lack aggregate initializers */ struct timespec timespec_of_parts (const time_t sec, const long nsec) { struct timespec ret = { sec, nsec}; return ret; } } /* [namespace] */ namespace type { /* * represent the clock id options from clock_gettime(2) as * a pair of enums. by default, CLOCK_MONOTONIC_COARSE is used * everywhere since that’s what we want in most cases. */ enum id { mono, /* CLOCK_MONOTONIC_COARSE */ real, /* CLOCK_REALIME_COARSE */ boot, /* CLOCK_BOOTTIME */ cpu, /* CLOCK_CPUTIME_* */ }; /* * for clocks that support it: non-coarse or raw variants; if a variant * does not apply to a given clock, it is ignored */ enum variant { dflt, /* all */ exact, /* mono, real: not (*_COARSE) */ raw, /* mono: _RAW */ process, /* cpu: *_PROCESS_* */ thread, /* cpu: *_THREAD_* */ }; } /* [namespace type] */ class Time { private: struct timespec value; public: enum type::id id; enum type::variant variant; int err; private: /* * Handle decimal part (nanosecond) overflow; this is performed * after arithmetic operations and whenever we there is the * possibility of unsanitized input for example in constructors * from other types and suchlike. * * POSIX defines the ns part as *long*. Technically, that means * that on machines where *sizeof long* equals *sizeof int*, it can * represent only up to around 2.1 seconds. In this range, the loop * version is most likely faster than division. However, since in * practice *long* is 8 bytes just about anywhere, we have to * handle greater dividends first. */ inline void carry_nsec (void) { # if LONG_BIT > 32 if ( this->value.tv_nsec < -3L * TIME_CONST_FACTOR_NANO || this->value.tv_nsec > 3L * TIME_CONST_FACTOR_NANO) { const long sec = this->value.tv_nsec / TIME_CONST_FACTOR_NANO; this->value.tv_nsec -= sec * TIME_CONST_FACTOR_NANO; this->value.tv_sec += sec; } # endif /* [LONG_BIT > 32] */ while (this->value.tv_nsec >= TIME_CONST_FACTOR_NANO) { this->value.tv_sec += 1; this->value.tv_nsec -= TIME_CONST_FACTOR_NANO; } while (this->value.tv_nsec < 0) { this->value.tv_sec -= 1; this->value.tv_nsec += TIME_CONST_FACTOR_NANO; } } /* ctors *************************************************************/ public: Time (const enum type::id id = type::mono, const enum type::variant var = type::dflt) NOEXCEPT; inline Time (const Time &t) NOEXCEPT : value (t.value) , id (t.id) , variant (t.variant) , err (t.err) { } inline Time (const time_t sec, const long nsec = 0, const enum type::id id = type::mono, const enum type::variant var = type::dflt, const int err = 0) NOEXCEPT : value (timespec_of_parts (sec, nsec)) , id (id) , variant (var) , err (err) { this->carry_nsec (); } explicit Time (const struct tm &tm, const enum type::id id = type::mono, const enum type::variant var = type::dflt); /* value read access *************************************************/ public: inline CONSTEXPR const struct timespec &get_time (void) const NOEXCEPT { return this->value; } inline CONSTEXPR const time_t &get_sec (void) const NOEXCEPT { return this->value.tv_sec; } inline CONSTEXPR const long &get_nsec (void) const NOEXCEPT { return this->value.tv_nsec; } inline CONSTEXPR const long get_msec (void) const NOEXCEPT { return this->get_nsec () / 1000000; } int64_t as_nanosec (void) const NOEXCEPT; long as_nanosec_L (void) const NOEXCEPT; /* value write access ************************************************/ public: inline void swap (Time &t) NOEXCEPT { std::swap (this->value , t.value ); std::swap (this->id , t.id ); std::swap (this->variant, t.variant); std::swap (this->err , t.err ); } Time &operator= (Time t) NOEXCEPT; Time &operator= (struct timespec ts) NOEXCEPT; inline void set (const struct timespec &ts) NOEXCEPT { this->value = ts; this->carry_nsec (); } inline void set (const time_t sec, const long nsec, const enum type::id id = type::mono, const enum type::variant var = type::dflt) NOEXCEPT { this->value.tv_sec = sec; this->value.tv_nsec = nsec; this->id = id; this->variant = var; this->carry_nsec (); } bool set (void) NOEXCEPT; void unset (void) NOEXCEPT; /* arithmetic ********************************************************/ public: Time &add (const time_t sec, const long nsec) NOEXCEPT; Time &subtract (const time_t sec, const long nsec) NOEXCEPT; inline Time &add (const Time &t2) NOEXCEPT { return this->add (t2.value.tv_sec, t2.value.tv_nsec); } inline Time &add (const time_t t2) NOEXCEPT { return this->add (t2, 0L); }; inline Time &subtract (const Time &t2) NOEXCEPT { return this->subtract (t2.value.tv_sec, t2.value.tv_nsec); } inline Time &subtract (const time_t t2) NOEXCEPT { return this->subtract (t2, 0L); }; Time &scale (const int64_t factor) NOEXCEPT; Time ÷ (const int64_t divisor) NOEXCEPT; friend int compare (const Time &t1, const Time &t2) NOEXCEPT; inline Time difference (const Time &t) NOEXCEPT { return (*this < t) ? t - *this : *this - t; } /* overloads *********************************************************/ public: inline Time operator+ (const Time &t2) const NOEXCEPT { return Time (*this).add (t2); } inline Time operator+ (const time_t t2) const NOEXCEPT { return Time (*this).add (t2); } inline Time & operator+= (const Time &t2) NOEXCEPT { return this->add (t2); } inline Time & operator+= (const time_t t2) NOEXCEPT { return this->add (t2); } inline Time operator- (const Time &t2) const NOEXCEPT { return Time (*this).subtract (t2); } inline Time operator- (const time_t t2) const NOEXCEPT { return Time (*this).subtract (t2); } inline Time & operator-= (const Time &t2) NOEXCEPT { return this->subtract (t2); } inline Time & operator-= (const time_t t2) NOEXCEPT { return this->subtract (t2); } inline Time operator* (const int64_t factor) const NOEXCEPT { return Time (*this).scale (factor); } inline Time & operator*= (const int64_t factor) NOEXCEPT { return this->scale (factor); } inline Time operator/ (const int64_t divisor) const NOEXCEPT { return Time (*this).divide (divisor); } inline Time & operator/= (const int64_t divisor) NOEXCEPT { return this->divide (divisor); } friend CONSTEXPR bool operator== (const Time &t1, const Time &t2) NOEXCEPT; friend CONSTEXPR bool operator< (const Time &t1, const Time &t2) NOEXCEPT; friend CONSTEXPR bool operator> (const Time &t1, const Time &t2) NOEXCEPT; friend std::ostream & operator<< (std::ostream &os, const Time &t); /* formatting ********************************************************/ public: boost::optional format_iso8601 (const bool utc = true, const bool date = true, const bool time = true, const bool tz = true) const; std::string make_nice_time (void) const; std::string format_full_time (void) const; std::string format_date (void) const; inline std::string format_sec_msec (void) const { return ::format_sec_msec (this->value); } inline boost::optional format_min_sec_msec (void) const { return ::format_min_sec_msec (this->value); } }; /* [class Time] */ inline Time operator+ (const time_t t1, const Time &t2) NOEXCEPT { return Time (t1) + t2; } inline Time operator- (const time_t t1, const Time &t2) NOEXCEPT { return Time (t1) - t2; } inline Time operator* (const time_t t1, const Time &t2) NOEXCEPT { return t2 * t1; } int compare (const Time &t1, const Time &t2) NOEXCEPT; /* * comparison for equality also considers the clock type; */ inline CONSTEXPR bool operator== (const Time &t1, const Time &t2) NOEXCEPT { return t1.id == t2.id && t1.variant == t2.variant && t1.value.tv_sec == t2.value.tv_sec && t1.value.tv_nsec == t2.value.tv_nsec ; } /* these ignore the *id* and *variant* fields */ inline CONSTEXPR bool operator< (const Time &t1, const Time &t2) NOEXCEPT { return t1.value.tv_sec < t2.value.tv_sec || ( t1.value.tv_sec == t2.value.tv_sec && t1.value.tv_nsec < t2.value.tv_nsec) ; } /* these ignore the *id* and *variant* fields */ inline CONSTEXPR bool operator> (const Time &t1, const Time &t2) NOEXCEPT { return t1.value.tv_sec > t2.value.tv_sec || ( t1.value.tv_sec == t2.value.tv_sec && t1.value.tv_nsec > t2.value.tv_nsec) ; } inline std::ostream & operator<< (std::ostream &os, const Time &t) { os << I2n::to_string (t.value.tv_sec) << "s, " << I2n::to_string (t.value.tv_nsec) << "ns" ; return os; } boost::optional