/** @file
* @brief time related functions.
*
- * @copyright Copyright © 2001-2008 by Intra2net AG
+ * @copyright Copyright © 2001-2018 by Intra2net AG
*/
#ifndef __TIMEFUNC_HXX
#define __TIMEFUNC_HXX
-#include <string>
+#include <climits>
+#include <errno.h>
#include <list>
+#include <string>
+#include <cstdlib>
+
+#include <boost/optional.hpp>
#include <week.hpp>
+#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<std::string> format_min_sec_msec (const struct timespec &ts);
+
+boost::optional<struct tm> scan_iso8601 (const char *s,
+ const bool date=true, const bool time=true,
+ const bool tz=true) NOEXCEPT;
+inline boost::optional<struct tm> 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);
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<std::string>
+ 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<std::string>
+ 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<Time>
+ now (const enum type::id id = type::mono,
+ const enum type::variant var = type::dflt) NOEXCEPT;
+
+ Time
+ zero (const enum type::id id = type::mono,
+ const enum type::variant var = type::dflt) NOEXCEPT;
+
+ boost::optional<Time>
+ time_of_iso8601 (const std::string &s,
+ const bool date = true,
+ const bool time = true,
+ const bool tz = true,
+ const enum type::id id = type::real,
+ const enum type::variant var = type::dflt) NOEXCEPT;
+
+ template <typename ContT>
+ Time
+ mean (const ContT &data)
+ {
+ Time sum (0, 0);
+
+ if (data.size () == 0) {
+ return sum;
+ }
+
+ for (typename ContT::const_iterator it = data.begin ();
+ it != data.end (); ++it)
+ {
+ sum += *it;
+ };
+
+ return sum.divide (static_cast<int64_t> (data.size ()));
+ };
+
+ template <typename ContT>
+ Time
+ median (const ContT &data)
+ {
+ if (data.size () == 0) {
+ return zero ();
+ }
+ if (data.size () == 1) {
+ return *data.begin ();
+ }
+
+ std::vector<typename ContT::value_type> sorted;
+ std::copy (data.begin (), data.end (), std::back_inserter (sorted));
+ std::sort (sorted.begin (), sorted.end ());
+
+ return sorted [data.size () / 2];
+ };
+
+} /* [namespace clock] */
+
+} /* [namespace I2n] */
#endif