*
*/
+#include <cstdio>
+#include <errno.h>
#include <string>
#include <sstream>
#include <iostream>
} // eo realtime_clock_gettime(long int&,long int&)
+/*
+ * There is a discrepancy of one input character
+ * due to the lack of sign handling in strptime(3):
+ *
+ * - strftime(3) needs the year specified as %5Y to account for the
+ * leading dash;
+ * - strptime(3) will not parse the leading dash with that format
+ * but apart from that it works well.
+ */
+
+namespace iso8601 {
+
+ /*
+ * max: -YYYYYYYYYY-MM-DDThh:mm:ssZ+zzzz ⇒ 32
+ * That is assuming the year in broken down time is an int we
+ * need to reserve ten decimal places.
+ */
+// static_assert (sizeof (((struct tm *)NULL)->tm_year) == 4);
+ static const size_t bufsize = 33;
+
+ enum kind {
+ d = 0,
+ t = 1,
+ tz = 2,
+ dt = 3,
+ dtz = 4,
+ ISO8601_SIZE = 5,
+ };
+
+ /*
+ * Unfortunately the glibc strptime(3) on the Intranator trips over
+ * the length specifier in field descriptors so we can’t reuse the
+ * formatters here. This problem is fixed in newer glibc. For the time
+ * being we keep two tables of formatters and choose the appropriate
+ * at runtime.
+ */
+
+ static const char *const formatter [ISO8601_SIZE] =
+ { /* [iso8601::d ] = */ "%4Y-%m-%d",
+ /* [iso8601::t ] = */ "%T",
+ /* [iso8601::tz ] = */ "%TZ%z",
+ /* [iso8601::dt ] = */ "%4Y-%m-%dT%T",
+ /* [iso8601::dtz] = */ "%4Y-%m-%dT%TZ%z",
+ };
+
+ static const char *const scanner [ISO8601_SIZE] =
+ { /* [iso8601::d ] = */ "%Y-%m-%d",
+ /* [iso8601::t ] = */ "%T",
+ /* [iso8601::tz ] = */ "%TZ%z",
+ /* [iso8601::dt ] = */ "%Y-%m-%dT%T",
+ /* [iso8601::dtz] = */ "%Y-%m-%dT%TZ%z",
+ };
+
+ static inline const char *
+ pick_fmt (const bool date, const bool time, const bool tz, const bool scan=false)
+ {
+ const char *const *table = scan ? iso8601::scanner : iso8601::formatter;
+ enum iso8601::kind format = iso8601::dtz;
+
+ if (date) {
+ if (time) {
+ if (tz) {
+ format = iso8601::dtz;
+ } else {
+ format = iso8601::dt;
+ }
+ } else {
+ format = iso8601::d;
+ }
+ } else if (time && tz) {
+ format = iso8601::tz;
+ } else {
+ format = iso8601::t; /* default to %T */
+ }
+
+ return table [format];
+ }
+
+} /* [iso8601] */
+
+
+namespace {
+
+ static inline int flip_tm_year (const int y)
+ { return (y + 1900) * -1 - 1900; }
+} /* [namespace] */
+
+/**
+ * @brief Format a time structure according to ISO-8601, e. g.
+ * “2018-01-09T10:40:00Z+0100”; see \c strftime(3) for
+ * the details.
+ *
+ * @param tm Time to format as broken-down \c struct tm.
+ * @param date Include the day part ([-]YYYY-MM-DD).
+ * @param time Include the time part (hh:mm:ss).
+ * @param tz Include the timezone ([±]ZZZZ); only needed if
+ * \c time is requested as well.
+ *
+ * @return The formatted timestamp.
+ */
+std::string format_iso8601 (const struct tm &tm, const bool date,
+ const bool time, const bool tz)
+{
+ struct tm tmp;
+ char buf [iso8601::bufsize] = { 0 };
+ char *start = &buf [0];
+ const char *format = iso8601::pick_fmt (date, time, tz);
+
+ memcpy (&tmp, &tm, sizeof (tmp));
+
+ if (tmp.tm_year < -1900) { /* negative year */
+ *start = '-';
+ start++;
+ tmp.tm_year = flip_tm_year (tmp.tm_year);
+ }
+
+ /*
+ * The sign is *always* handled above so the formatted string here
+ * is always one character shorter.
+ */
+ if (strftime (start, iso8601::bufsize-1, format, &tmp) == 0)
+ {
+ return std::string ();
+ }
+
+ buf [iso8601::bufsize-1] = '\0'; /* Just in case. */
+
+ return std::string (buf);
+}
+
+typedef struct tm * (*time_breakdown_fn) (const time_t *, struct tm *);
+
+/**
+ * @brief Format a UNIX timestamp according to ISO-8601. Converts
+ * to broken down time first.
+ *
+ * @param t Time to format as broken-down \c struct tm.
+ * @param date Include the day part ([-]YYYY-MM-DD).
+ * @param time Include the time part (hh:mm:ss).
+ * @param tz Include the timezone ([±]ZZZZ); only heeded if
+ * \c time is requested as well.
+ *
+ * @return The formatted timestamp.
+ */
+std::string format_iso8601 (time_t t, const bool utc, const bool date,
+ const bool time, const bool tz)
+{
+ time_breakdown_fn breakdown = utc ? gmtime_r : localtime_r;
+ struct tm tm;
+
+ errno = 0;
+ if (breakdown (&t, &tm) == NULL) {
+ return std::string ("error analyzing timestamp: ") + strerror (errno);
+ }
+
+ return format_iso8601 (tm, date, time, tz);
+}
+
+/**
+ * @brief Read a ISO-8601 formatted date stamp into broken down time.
+ *
+ * @param s String containing the timestamp.
+ *
+ * @return \c boost::none if the input string was \c NULL or malformed,
+ * an optional \c struct tm with the extracted values otherwise.
+ */
+boost::optional<struct tm>
+scan_iso8601 (const char *s,
+ const bool date, const bool time, const bool tz) NOEXCEPT
+{
+ struct tm tm;
+ const char *format = iso8601::pick_fmt (date, time, tz, true);
+ const char *start = s;
+ bool negyear = false;
+
+ if (s == NULL) {
+ return boost::none;
+ }
+
+ switch (s [0]) {
+ case '\0': {
+ return boost::none;
+ break;
+ }
+ /*
+ * Contrary to what the man page indicates, strptime(3) is *not*
+ * the inverse operation of strftime(3)! The later correctly formats
+ * negative year numbers with the %F modifier wheres the former trips
+ * over the sign character.
+ */
+ case '-': {
+ negyear = true;
+ start++;
+ break;
+ }
+ default: {
+ break;
+ }
+ }
+
+ memset (&tm, 0, sizeof (tm));
+
+ if (strptime (start, format, &tm) == NULL) {
+ return boost::none;
+ }
+
+ if (negyear) {
+ tm.tm_year = flip_tm_year (tm.tm_year);
+ }
+
+ return tm;
+}
+
+/**
+ * @brief Format a \c struct timespec in the schema established by
+ * time(1): “3m14.159s”.
+ *
+ * @param ts The time spec to format.
+ *
+ * @return \c boost:none in case of error during formatting, an optional
+ * \c std::string otherwise.
+ */
+boost::optional<std::string>
+format_min_sec_msec (const struct timespec &ts)
+{
+ char ms [4] = { '\0', '\0', '\0', '\0' };
+
+ if (snprintf (ms, 4, "%.3ld", ts.tv_nsec / 1000000) < 0) {
+ return boost::none;
+ }
+
+ const time_t min = ts.tv_sec / 60;
+ const time_t sec = ts.tv_sec - min * 60;
+
+ return I2n::to_string (min) + "m"
+ + I2n::to_string (sec) + "."
+ + ms + "s"
+ ;
+}
+
+namespace I2n {
+
+namespace clock {
+
+ namespace {
+
+ /**
+ * @brief <b>For internal use only</b>. Translates clock
+ * specification flags to kernel clock types.
+ *
+ * @param id Master clock id: \c mono, \c real, \c boot, or \c
+ * cpu.
+ * @param var Variant of clock if appropriate: \c raw, \c exact, \c
+ * process, or \c thread. Use \c dflt for the base
+ * variant.
+ *
+ * @return The clock id for using with kernel APIs.
+ */
+ static inline clockid_t
+ clockid_of_flags (const enum type::id id,
+ const enum type::variant var) NOEXCEPT
+ {
+ clockid_t cid = CLOCK_MONOTONIC_COARSE;
+
+ switch (id) {
+
+ default:
+ case type::mono: {
+ switch (var) {
+ default: {
+ break;
+ }
+ case type::raw: {
+ cid = CLOCK_MONOTONIC_RAW;
+ break;
+ }
+ case type::exact: {
+ cid = CLOCK_MONOTONIC;
+ break;
+ }
+ }
+ break;
+ }
+
+ case type::real: {
+ if (var == type::exact) {
+ cid = CLOCK_REALTIME;
+ } else {
+ cid = CLOCK_REALTIME_COARSE;
+ }
+ break;
+ }
+
+ case type::boot: {
+ cid = CLOCK_BOOTTIME;
+ break;
+ }
+
+ case type::cpu: {
+ if (var == type::thread) {
+ cid = CLOCK_THREAD_CPUTIME_ID;
+ } else {
+ cid = CLOCK_PROCESS_CPUTIME_ID;
+ }
+ break;
+ }
+ } /* [switch id] */
+
+ return cid;
+ }
+
+ static const struct timespec zero_time = { 0, 0 };
+
+ } /* [namespace] */
+
+ Time::Time (const enum type::id id,
+ const enum type::variant var) NOEXCEPT
+ : value (zero_time)
+ , id (id)
+ , variant (var)
+ , err (0)
+ { }
+
+ /*
+ * Ctor from *struct tm*. On 32 bit systems the conversion to *time_t* will
+ * fail with years outside the range from epoch to 2038.
+ */
+ Time::Time (const struct tm &tm,
+ const enum type::id id,
+ const enum type::variant var)
+ {
+ struct tm tmp_tm; /* dummy for mktime(3) */
+ Time tmp_time;
+
+ memcpy (&tmp_tm, &tm, sizeof (tmp_tm));
+
+ errno = 0;
+ const time_t t = mktime (&tmp_tm);
+ if (t == - 1) { /* Glibc does not set errno on out-of-range here! */
+ const char *datestr = asctime (&tm);
+ throw conversion_error (errno,
+ std::string ("mktime: from struct tm {")
+ + std::string (datestr, 0, strlen(datestr)-1)
+ + "}");
+ }
+
+ tmp_time = Time (t, 0l, id, var);
+
+ this->swap (tmp_time);
+ }
+
+ int64_t
+ Time::as_nanosec (void) const NOEXCEPT
+ {
+ return int64_t (this->value.tv_sec) * TIME_CONST_FACTOR_NANO
+ + this->value.tv_nsec;
+ }
+
+ long
+ Time::as_nanosec_L (void) const NOEXCEPT /* likely to overflow */
+ { return static_cast<long>(this->as_nanosec ()); }
+
+ Time &
+ Time::operator= (Time t2) NOEXCEPT
+ {
+ this->swap (t2);
+
+ return *this;
+ }
+
+ /*
+ * @note This operator is an up-assignment from a type containing less
+ * information than the structure assigned from. Since the
+ * operator can only be two valued we must normalize the remaining
+ * fields to the default clock. When assigning from non-default
+ * clocks, use the appropriate constructor and pass it the desired
+ * id and variant so as to assign the result.
+ */
+ Time &
+ Time::operator= (struct timespec ts) NOEXCEPT
+ {
+ std::swap (this->value, ts);
+ this->id = clock::type::mono;
+ this->variant = clock::type::dflt;
+ this->err = 0;
+
+ return *this;
+ }
+
+ void
+ Time::unset (void) NOEXCEPT
+ { this->value = zero_time; }
+
+ bool
+ Time::set (void) NOEXCEPT
+ {
+ struct timespec now;
+
+ errno = 0;
+ if (clock_gettime (clockid_of_flags (this->id, this->variant), &now)
+ == -1)
+ {
+ this->err = errno;
+ this->unset ();
+
+ return false;
+ }
+ this->err = 0;
+ this->value = now;
+
+ return true;
+ }
+
+ Time &
+ Time::add (const time_t sec, const long nsec) NOEXCEPT
+ {
+ this->value.tv_sec += sec;
+ this->value.tv_nsec += nsec;
+
+ this->carry_nsec ();
+
+ return *this;
+ }
+
+ Time &
+ Time::subtract (const time_t sec, const long nsec) NOEXCEPT
+ {
+ this->value.tv_sec -= sec;
+ this->value.tv_nsec -= nsec;
+
+ this->carry_nsec ();
+
+ return *this;
+ }
+
+ Time &
+ Time::scale (const int64_t factor) NOEXCEPT
+ {
+ this->value.tv_sec *= factor;
+ this->value.tv_nsec *= factor;
+
+ this->carry_nsec ();
+
+ return *this;
+ }
+
+ /*
+ * Below division code purposely does not attempt to handle divide-
+ * by-zero just as any other C++ division function does. It is up to
+ * the caller to ensure that the divisor is not zero.
+ */
+ Time &
+ Time::divide (const int64_t divisor) NOEXCEPT
+ {
+ const long sec = static_cast<long> (this->value.tv_sec );
+ int64_t nsec = static_cast<int64_t> (this->value.tv_nsec);
+ const ldiv_t div = ldiv (sec, divisor);
+
+ if (div.rem != 0) {
+ nsec += div.rem * TIME_CONST_FACTOR_NANO;
+ }
+
+ nsec /= divisor;
+
+ this->value.tv_sec = static_cast<time_t> (div.quot);
+ this->value.tv_nsec = static_cast<long> (nsec);
+
+ this->carry_nsec ();
+
+ return *this;
+ }
+
+ /**
+ * @brief Format timestamp according to the ISO standard rules.
+ *
+ * @param utc Whether to normalize the timestamp to UTC or local time.
+ * @param date Whether to include the date (%F).
+ * @param time Whether to include the time (%T).
+ * @param tz Whether to include the UTC offset (%z).
+ *
+ * @return \c none if the formatting operation failed, the
+ * formatted timestamp otherwise.
+ *
+ * @note The standard allows for extending the format using
+ * a fractional component. However, this is subject to
+ * local conventions so we don’t support it. For more
+ * than seconds granularity use a better suited format
+ * like LDAP Generalized time instead.
+ */
+ boost::optional<std::string>
+ Time::format_iso8601 (const bool utc,
+ const bool date,
+ const bool time,
+ const bool tz) const
+ {
+ time_breakdown_fn breakdown = utc ? gmtime_r : localtime_r;
+ struct tm tm;
+
+ if (breakdown (&this->value.tv_sec, &tm) == NULL) {
+ return boost::none;
+ }
+
+ return ::format_iso8601 (tm, date, time, tz);
+ }
+
+ std::string
+ Time::make_nice_time (void) const
+ {
+ /* XXX the cast below results in loss of precision with 64 bit time_t! */
+ return ::make_nice_time (static_cast<int> (this->value.tv_sec));
+ }
+
+ std::string
+ Time::format_full_time (void) const
+ { return ::format_full_time (this->value.tv_sec); }
+
+ std::string
+ Time::format_date (void) const
+ { return ::format_date (this->value.tv_sec); }
+
+ /**
+ * @brief Obtain the current time wrt. the given
+ * clock variants.
+ *
+ * @param id Clock id.
+ * @param var Clock variant.
+ *
+ * @return \c none if the underlying \c clock_gettime() operation
+ * failed, a fully initialized \c struct Time otherwise.
+ */
+ boost::optional<Time>
+ now (const enum type::id id, const enum type::variant var) NOEXCEPT
+ {
+ Time ret (id, var);
+
+ if (!ret.set ()) {
+ return boost::none;
+ }
+
+ return ret;
+ }
+
+ Time
+ zero (const enum type::id id, const enum type::variant var) NOEXCEPT
+ { return Time (id, var); }
+
+ /**
+ * @brief Standard three-way comparison for \c struct Time
+ * relying on strict total ordering.
+ *
+ * @param t1 Comparand.
+ * @param t2 Comparand.
+ *
+ * @return -1, 0, 1 depending on whether t1 is less-than, equal,
+ * or greater than t2.
+ *
+ * @note This should be used to implement the spaceship operator
+ * (P0515R0) when we get a new compiler.
+ */
+ int
+ compare (const Time &t1, const Time &t2) NOEXCEPT
+ {
+ if (t1.value.tv_sec < t2.value.tv_sec) {
+ return -1;
+ }
+
+ if (t1.value.tv_sec > t2.value.tv_sec) {
+ return 1;
+ }
+
+ if (t1.value.tv_nsec < t2.value.tv_nsec) {
+ return -1;
+ }
+
+ if (t1.value.tv_nsec > t2.value.tv_nsec) {
+ return 1;
+ }
+
+ return 0;
+ }
+
+ /**
+ * @brief Interpret string as timestamp according to the ISO
+ * standard rules.
+ *
+ * This is the inverse operation of \c format_iso8601().
+ *
+ * @param s Input string to read. The entire string is interpreted
+ * and it must not contain any trailing data.
+ * @param date Whether to parse the date (%F).
+ * @param time Whether to parse the time (%T).
+ * @param tz Whether to parse the UTC offset (%z).
+ * @param id Clock id to assign the result.
+ * @param var Clock variant to assign the result.
+ *
+ * @return \c none if the input could not be parsed according to
+ * ISO rules, a \c struct Time otherwise.
+ */
+ boost::optional<Time>
+ time_of_iso8601 (const std::string &s,
+ const bool date,
+ const bool time,
+ const bool tz,
+ const enum type::id id,
+ const enum type::variant var) NOEXCEPT
+ {
+ boost::optional<struct tm> tm = scan_iso8601 (s, date, time, tz);
+
+ if (!tm) {
+ return boost::none;
+ }
+
+ try {
+ return Time (*tm, id, var);
+ }
+ catch (conversion_error &_unused) { }
+
+ return boost::none;
+ }
+
+} /* [namespace clock] */
+
+} /* [namespace I2n] */
/** @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
#include <unistd.h>
#include <set>
+#include <iostream>
using namespace std;
using namespace I2n;
protected:
typedef std::list< std::string > StringList;
std::set<std::string> used_check_files;
+ std::string tz; /* save and restore TZ from envp */
std::string get_check_file_path(std::string tag)
{
used_check_files.clear();
} // eo remove_check_files
+ void set_utc (void)
+ {
+ errno = 0;
+ if (setenv ("TZ", "UTC", 1) == -1)
+ {
+ std::cerr
+ << "error setting environment 'TZ': [" << this->tz << "]"
+ << std::endl
+ ;
+ return;
+ }
+
+ tzset ();
+ }
+
public:
TestTimeFuncFixture()
+ : tz (secure_getenv ("TZ") ?: "")
{
}
~TestTimeFuncFixture()
{
remove_check_files();
+
+ errno = 0;
+ if (setenv ("TZ", this->tz.c_str (), 1) == -1)
+ {
+ std::cerr
+ << "error cleaning up environment 'TZ': [" << this->tz << "]"
+ << std::endl
+ ;
+ }
+ else
+ {
+ tzset();
+ }
}
};
BOOST_CHECK_EQUAL(1341093600, date_to_seconds("2012-07-01"));
}
+BOOST_AUTO_TEST_CASE(FormatISO8601_T)
+{
+ const time_t moment = 1515492684;
+ BOOST_CHECK_EQUAL("10:11:24",
+ format_iso8601 (moment, true, false, true, false));
+}
+
+BOOST_AUTO_TEST_CASE(FormatISO8601_TZ_local)
+{
+ const time_t moment = 1515492684;
+ BOOST_CHECK_EQUAL("11:11:24Z+0100",
+ format_iso8601 (moment, false, false, true, true));
+}
+
+BOOST_AUTO_TEST_CASE(FormatISO8601_TZ)
+{
+ const time_t moment = 1515492684;
+ BOOST_CHECK_EQUAL("10:11:24Z+0000",
+ format_iso8601 (moment, true, false, true, true));
+}
+
+BOOST_AUTO_TEST_CASE(FormatISO8601_DTZ_local)
+{
+ const time_t moment = 1515492684;
+ BOOST_CHECK_EQUAL("2018-01-09T11:11:24Z+0100",
+ format_iso8601 (moment, false, true, true, true));
+}
+
+BOOST_AUTO_TEST_CASE(FormatISO8601_DTZ)
+{
+ const time_t moment = 1515492684;
+ BOOST_CHECK_EQUAL("2018-01-09T10:11:24Z+0000",
+ format_iso8601 (moment, true, true, true, true));
+}
+
+BOOST_AUTO_TEST_CASE(FormatISO8601_DT)
+{
+ const time_t moment = 1515492684;
+ BOOST_CHECK_EQUAL("2018-01-09T10:11:24",
+ format_iso8601 (moment, true, true, true, false));
+}
+
+BOOST_AUTO_TEST_CASE(FormatISO8601_D)
+{
+ const time_t moment = 1515492684;
+ BOOST_CHECK_EQUAL("2018-01-09",
+ format_iso8601 (moment, true, true, false, false));
+}
+
+BOOST_AUTO_TEST_CASE(FormatISO8601_DTZ_struct_tm)
+{
+ struct tm helau;
+ helau.tm_sec = 11;
+ helau.tm_min = 11;
+ helau.tm_hour = 11;
+ helau.tm_mday = 11;
+ helau.tm_mon = 10;
+ helau.tm_year = 2018 - 1900;
+ helau.tm_wday = 0;
+ helau.tm_yday = 315;
+ helau.tm_isdst = 0;
+ helau.tm_gmtoff = 0;
+ helau.tm_zone = NULL;
+
+ BOOST_CHECK_EQUAL("2018-11-11T11:11:11Z+0000",
+ format_iso8601 (helau, true, true, true));
+}
+
+BOOST_AUTO_TEST_CASE(FormatISO8601_DTZ_struct_timespec)
+{
+ struct timespec ts = { 1541934671, 11 };
+
+ BOOST_CHECK_EQUAL("2018-11-11T11:11:11Z+0000",
+ format_iso8601 (ts, true, true, true, true));
+}
+
+BOOST_AUTO_TEST_SUITE(Clock)
+
+ BOOST_AUTO_TEST_CASE(ctor_simple)
+ {
+ I2n::clock::Time t;
+
+ BOOST_CHECK_EQUAL(t.get_sec (), 0);
+ BOOST_CHECK_EQUAL(t.get_nsec (), 0);
+ }
+
+ BOOST_AUTO_TEST_CASE(ctor_type)
+ {
+ I2n::clock::Time t1 (I2n::clock::type::real);
+ I2n::clock::Time t2 (I2n::clock::type::mono);
+ I2n::clock::Time t3 (I2n::clock::type::boot);
+ I2n::clock::Time t4 (I2n::clock::type::cpu);
+
+ BOOST_CHECK_EQUAL(t1.get_sec (), 0);
+ BOOST_CHECK_EQUAL(t1.get_nsec (), 0);
+
+ BOOST_CHECK_EQUAL(t2.get_sec (), 0);
+ BOOST_CHECK_EQUAL(t2.get_nsec (), 0);
+
+ BOOST_CHECK_EQUAL(t3.get_sec (), 0);
+ BOOST_CHECK_EQUAL(t3.get_nsec (), 0);
+
+ BOOST_CHECK_EQUAL(t4.get_sec (), 0);
+ BOOST_CHECK_EQUAL(t4.get_nsec (), 0);
+ }
+
+ BOOST_AUTO_TEST_CASE(ctor_variant)
+ {
+ I2n::clock::Time tmc (I2n::clock::type::mono);
+ I2n::clock::Time tmr (I2n::clock::type::mono, I2n::clock::type::raw);
+ I2n::clock::Time tme (I2n::clock::type::mono, I2n::clock::type::exact);
+
+ I2n::clock::Time trc (I2n::clock::type::real);
+ I2n::clock::Time tre (I2n::clock::type::real, I2n::clock::type::exact);
+
+ I2n::clock::Time tb (I2n::clock::type::boot);
+
+ I2n::clock::Time tcp (I2n::clock::type::cpu);
+ I2n::clock::Time tct (I2n::clock::type::cpu, I2n::clock::type::thread);
+
+ BOOST_CHECK_EQUAL(tmc.get_sec (), 0); /* MONO */
+ BOOST_CHECK_EQUAL(tmc.get_nsec (), 0);
+
+ BOOST_CHECK_EQUAL(tmr.get_sec (), 0);
+ BOOST_CHECK_EQUAL(tmr.get_nsec (), 0);
+
+ BOOST_CHECK_EQUAL(tme.get_sec (), 0);
+ BOOST_CHECK_EQUAL(tme.get_nsec (), 0);
+
+ BOOST_CHECK_EQUAL(trc.get_sec (), 0); /* REAL */
+ BOOST_CHECK_EQUAL(trc.get_nsec (), 0);
+
+ BOOST_CHECK_EQUAL(tre.get_sec (), 0);
+ BOOST_CHECK_EQUAL(tre.get_nsec (), 0);
+
+ BOOST_CHECK_EQUAL(tb.get_sec (), 0); /* BOOT */
+ BOOST_CHECK_EQUAL(tb.get_nsec (), 0);
+
+ BOOST_CHECK_EQUAL(tcp.get_sec (), 0); /* CPU */
+ BOOST_CHECK_EQUAL(tcp.get_nsec (), 0);
+
+ BOOST_CHECK_EQUAL(tct.get_sec (), 0);
+ BOOST_CHECK_EQUAL(tct.get_nsec (), 0);
+ }
+
+ BOOST_AUTO_TEST_CASE(initializer_now)
+ {
+ boost::optional<I2n::clock::Time> t = I2n::clock::now ();
+
+ BOOST_CHECK(t);
+ BOOST_CHECK_GT(t->get_sec (), 0);
+ BOOST_CHECK_EQUAL(t->err, 0);
+ }
+
+ BOOST_AUTO_TEST_CASE(initializer_zero)
+ {
+ I2n::clock::Time stundenull = I2n::clock::zero ();
+
+ BOOST_CHECK_EQUAL(stundenull.get_sec (), 0);
+ BOOST_CHECK_EQUAL(stundenull.get_nsec (), 0);
+ BOOST_CHECK_EQUAL(stundenull.err, 0);
+ }
+
+ BOOST_AUTO_TEST_CASE(member_set_now)
+ {
+ I2n::clock::Time t;
+
+ BOOST_CHECK(t.set ());
+
+ BOOST_CHECK_NE(t.get_sec (), 0);
+ }
+
+ BOOST_AUTO_TEST_CASE(member_set_value)
+ {
+ I2n::clock::Time t;
+
+ t.set (42, 42);
+
+ BOOST_CHECK_EQUAL(t.get_sec (), 42);
+ BOOST_CHECK_EQUAL(t.get_nsec (), 42);
+ }
+
+ BOOST_AUTO_TEST_CASE(member_set_value_type)
+ {
+ I2n::clock::Time t;
+
+ t.set (42, 42, I2n::clock::type::real, I2n::clock::type::exact);
+
+ BOOST_CHECK_EQUAL(t.get_sec (), 42);
+ BOOST_CHECK_EQUAL(t.get_nsec (), 42);
+ }
+
+ BOOST_AUTO_TEST_CASE(member_add_parts)
+ {
+ I2n::clock::Time t;
+
+ t.set (42, 42);
+ t.add (2187, 2187);
+
+ BOOST_CHECK_EQUAL(t.get_sec (), 2229);
+ BOOST_CHECK_EQUAL(t.get_nsec (), 2229);
+ }
+
+ BOOST_AUTO_TEST_CASE(member_sub_parts)
+ {
+ I2n::clock::Time t;
+
+ t.set (2, 0L);
+ t.subtract (1, 1L);
+
+ BOOST_CHECK_EQUAL(t.get_sec (), 0);
+ BOOST_CHECK_EQUAL(t.get_nsec (), 999999999);
+ }
+
+ BOOST_AUTO_TEST_CASE(member_sub_Time)
+ {
+ I2n::clock::Time t1;
+ I2n::clock::Time t2;
+
+ t1.set (42, 42L);
+ t2.set (42, 0L);
+
+ t1.subtract (t2);
+
+ BOOST_CHECK_EQUAL(t1.get_sec (), 0);
+ BOOST_CHECK_EQUAL(t1.get_nsec (), 42L);
+ }
+
+ BOOST_AUTO_TEST_CASE(member_diff)
+ {
+ static const time_t five = 5 * 365 * 24 * 3600;
+
+ I2n::clock::Time t1 (42, 1337);
+ I2n::clock::Time t2 = t1 + five;;
+ I2n::clock::Time t3 = t1 - five;;
+
+ BOOST_CHECK_EQUAL(t2, I2n::clock::Time ((time_t)42 + five, 1337));
+ BOOST_CHECK_EQUAL(t3, I2n::clock::Time ((time_t)42 - five, 1337));
+ BOOST_CHECK_EQUAL(t1.difference (t3), t3.difference (t1));
+ BOOST_CHECK_EQUAL(t3.difference (t3), t3.difference (t3));
+ }
+
+ BOOST_AUTO_TEST_CASE(op_copyassign)
+ {
+ I2n::clock::Time t1;
+ I2n::clock::Time t2;
+
+ BOOST_CHECK(t1.set ());
+
+ t2 = t1;
+
+ BOOST_CHECK_EQUAL(t1.get_sec (), t2.get_sec ());
+ BOOST_CHECK_EQUAL(t1.get_nsec (), t2.get_nsec ());
+ }
+
+ BOOST_AUTO_TEST_CASE(op_equal)
+ {
+ I2n::clock::Time t1;
+ I2n::clock::Time t2;
+
+ BOOST_CHECK(t1.set ());
+ t2 = t1;
+
+ BOOST_CHECK_EQUAL(t1, t2);
+ }
+
+ BOOST_AUTO_TEST_CASE(op_add_Time)
+ {
+ I2n::clock::Time t1;
+ I2n::clock::Time t2;
+ I2n::clock::Time tsum;
+
+ t1.set (2187, 2187);
+ t2.set (1337, 1337);
+ tsum = t1 + t2;
+
+ BOOST_CHECK_EQUAL(tsum.get_sec (), 3524);
+ BOOST_CHECK_EQUAL(tsum.get_nsec (), 3524);
+ }
+
+ BOOST_AUTO_TEST_CASE(op_add_Time_carry)
+ {
+ I2n::clock::Time t1;
+ I2n::clock::Time t2;
+ I2n::clock::Time tsum;
+
+ t1.set (2187, 2187);
+# if LONG_BIT == 32
+ t2.set (1300, 2L * 1000 * 1000 * 1000 + 1337);
+# else
+ t2.set (1300, 37L * 1000 * 1000 * 1000 + 1337);
+# endif
+
+ tsum = t1 + t2;
+
+# if LONG_BIT == 32
+ BOOST_CHECK_EQUAL(tsum.get_sec (), 3489);
+ BOOST_CHECK_EQUAL(tsum.get_nsec (), 3524);
+# else
+ BOOST_CHECK_EQUAL(tsum.get_sec (), 3524);
+ BOOST_CHECK_EQUAL(tsum.get_nsec (), 3524);
+# endif
+ }
+
+ BOOST_AUTO_TEST_CASE(op_add_time_t)
+ {
+ I2n::clock::Time t1 (2187, 2187);
+ time_t t2 = 1337;
+ I2n::clock::Time tsum;
+
+ tsum = t1 + t2;
+
+ BOOST_CHECK_EQUAL(tsum.get_sec (), 3524);
+ BOOST_CHECK_EQUAL(tsum.get_nsec (), 2187);
+ }
+
+ BOOST_AUTO_TEST_CASE(op_add_time_t_external)
+ {
+ time_t t1 = 1337;
+ I2n::clock::Time t2 (2187, 2187);
+ I2n::clock::Time tsum;
+
+ tsum = t1 + t2;
+
+ BOOST_CHECK_EQUAL(tsum.get_sec (), 3524);
+ BOOST_CHECK_EQUAL(tsum.get_nsec (), 2187);
+ }
+
+ BOOST_AUTO_TEST_CASE(op_incr_Time)
+ {
+ I2n::clock::Time t1 (2187, 2187);
+ I2n::clock::Time t2 (1337, 1337);
+
+ t1 += t2;
+
+ BOOST_CHECK_EQUAL(t1.get_sec (), 3524);
+ BOOST_CHECK_EQUAL(t1.get_nsec (), 3524);
+ }
+
+ BOOST_AUTO_TEST_CASE(op_incr_time_t)
+ {
+ I2n::clock::Time t1 (2187, 2187);
+ time_t t2 = 1337;
+
+ t1 += t2;
+
+ BOOST_CHECK_EQUAL(t1.get_sec (), 3524);
+ BOOST_CHECK_EQUAL(t1.get_nsec (), 2187);
+ }
+
+ BOOST_AUTO_TEST_CASE(op_subtract_Time)
+ {
+ I2n::clock::Time t1;
+ I2n::clock::Time t2;
+ I2n::clock::Time tdiff;
+
+ t1.set (2187, 2187);
+ t2.set (1337, 1337);
+ tdiff = t1 - t2;
+
+ BOOST_CHECK_EQUAL(tdiff.get_sec (), 850);
+ BOOST_CHECK_EQUAL(tdiff.get_nsec (), 850);
+ }
+
+ BOOST_AUTO_TEST_CASE(op_subtract_time_t)
+ {
+ I2n::clock::Time t1 (2187, 2187);
+ time_t t2 = 1337;
+ I2n::clock::Time tdiff;
+
+ tdiff = t1 - t2;
+
+ BOOST_CHECK_EQUAL(tdiff.get_sec (), 850);
+ BOOST_CHECK_EQUAL(tdiff.get_nsec (), 2187);
+ }
+
+ BOOST_AUTO_TEST_CASE(op_subtract_time_t_external)
+ {
+ time_t t1 = 1337;
+ I2n::clock::Time t2 (2187, 2187);
+ I2n::clock::Time tdiff;
+
+ tdiff = t1 - t2;
+
+ BOOST_CHECK_EQUAL(tdiff.get_sec (), -851);
+ BOOST_CHECK_EQUAL(tdiff.get_nsec (), 999997813);
+ }
+
+ BOOST_AUTO_TEST_CASE(op_decr_Time)
+ {
+ I2n::clock::Time t1 (2187, 2187);
+ I2n::clock::Time t2 (1337, 1337);
+
+ t1 -= t2;
+
+ BOOST_CHECK_EQUAL(t1.get_sec (), 850);
+ BOOST_CHECK_EQUAL(t1.get_nsec (), 850);
+ }
+
+ BOOST_AUTO_TEST_CASE(op_decr_time_t)
+ {
+ I2n::clock::Time t1 (2187, 2187);
+ time_t t2 = 1337;
+
+ t1 -= t2;
+
+ BOOST_CHECK_EQUAL(t1.get_sec (), 850);
+ BOOST_CHECK_EQUAL(t1.get_nsec (), 2187);
+ }
+
+ BOOST_AUTO_TEST_CASE(op_mult_scale)
+ {
+ I2n::clock::Time t1;
+ I2n::clock::Time t2;
+
+ t1.set (1, 1);
+ t2 = t1 * 42;
+
+ BOOST_CHECK_EQUAL(t2.get_sec (), 42);
+ BOOST_CHECK_EQUAL(t2.get_nsec (), 42);
+ }
+
+ BOOST_AUTO_TEST_CASE(op_mult_mutate)
+ {
+ I2n::clock::Time t1 ( 42, 42);
+ I2n::clock::Time t2 (1337, 0);
+
+ t1 *= 2;
+ t2 *= -10;
+
+ BOOST_CHECK_EQUAL(t1.get_sec (), 84);
+ BOOST_CHECK_EQUAL(t1.get_nsec (), 84);
+ BOOST_CHECK_EQUAL(t2.get_sec (), -13370);
+ }
+
+ BOOST_AUTO_TEST_CASE(op_mult_scale_carry)
+ {
+ I2n::clock::Time t1;
+ I2n::clock::Time t2;
+
+ t1.set (1, 500 * 1000 * 1000);
+ t2 = t1 * 3;
+
+ BOOST_CHECK_EQUAL(t2.get_sec (), 4);
+ BOOST_CHECK_EQUAL(t2.get_nsec (), 500 * 1000 * 1000);
+ }
+
+ BOOST_AUTO_TEST_CASE(op_equals)
+ {
+ I2n::clock::Time t1;
+ I2n::clock::Time t2;
+
+ t1.set (50, 50);
+ t2.set (50, 50);
+
+ BOOST_CHECK_EQUAL(t1, t2);
+ }
+
+ BOOST_AUTO_TEST_CASE(compare_equal)
+ {
+ I2n::clock::Time t1;
+ I2n::clock::Time t2;
+
+ t1.set (42, 42);
+ t2.set (42, 42);
+
+ BOOST_CHECK_EQUAL(I2n::clock::compare (t1, t2), 0);
+ }
+
+ BOOST_AUTO_TEST_CASE(compare_equal_type)
+ {
+ I2n::clock::Time t1 (42, 42, I2n::clock::type::real);
+ I2n::clock::Time t2 (42, 42, I2n::clock::type::cpu);
+ I2n::clock::Time t3 (42, 0, I2n::clock::type::real);
+ I2n::clock::Time t4 (42, 42, I2n::clock::type::real);
+
+ BOOST_CHECK_NE(t1, t2);
+ BOOST_CHECK_NE(t1, t3);
+ BOOST_CHECK_EQUAL(t1, t4);
+ }
+
+ BOOST_AUTO_TEST_CASE(compare_ne_sec)
+ {
+ I2n::clock::Time t1;
+ I2n::clock::Time t2;
+
+ t1.set ( 42, 42);
+ t2.set (1337, 42);
+
+ BOOST_CHECK_EQUAL(I2n::clock::compare (t1, t2), -1);
+ BOOST_CHECK_EQUAL(I2n::clock::compare (t2, t1), 1);
+ }
+
+ BOOST_AUTO_TEST_CASE(compare_ne_nsec)
+ {
+ I2n::clock::Time t1;
+ I2n::clock::Time t2;
+
+ t1.set (42, 42);
+ t2.set (42, 1337);
+
+ BOOST_CHECK_EQUAL(I2n::clock::compare (t1, t2), -1);
+ BOOST_CHECK_EQUAL(I2n::clock::compare (t2, t1), 1);
+ }
+
+ BOOST_AUTO_TEST_CASE(compare_ne_both)
+ {
+ I2n::clock::Time t1;
+ I2n::clock::Time t2;
+
+ t1.set (42, 2187);
+ t2.set (23, 1337);
+
+ BOOST_CHECK_EQUAL(I2n::clock::compare (t1, t2), 1);
+ BOOST_CHECK_EQUAL(I2n::clock::compare (t2, t1), -1);
+ }
+
+ BOOST_AUTO_TEST_CASE(op_ineq_sec)
+ {
+ I2n::clock::Time t1 (1337);
+ I2n::clock::Time t2 (2187);
+
+ BOOST_CHECK_LT(t1, t2);
+ BOOST_CHECK_GT(t2, t1);
+ }
+
+ BOOST_AUTO_TEST_CASE(op_ineq_nsec)
+ {
+ I2n::clock::Time t1 (1337, 23);
+ I2n::clock::Time t2 (1337, 42);
+
+ BOOST_CHECK_LT(t1, t2);
+ BOOST_CHECK_GT(t2, t1);
+ }
+
+ BOOST_AUTO_TEST_CASE(op_ineq_both)
+ {
+ I2n::clock::Time t1 (2187, 23);
+ I2n::clock::Time t2 (1337, 42);
+
+ BOOST_CHECK_LT(t2, t1);
+ BOOST_CHECK_GT(t1, t2);
+ }
+
+ BOOST_AUTO_TEST_CASE(op_eq_time_t)
+ {
+ boost::optional<I2n::clock::Time> t1 = I2n::clock::now ();
+ const time_t t2 = time (NULL); /* race here */
+
+ *t1 -= (time_t)42;
+
+ BOOST_CHECK_NE(*t1, t2);
+ BOOST_CHECK_LT(*t1, t2);
+ BOOST_CHECK_GT( t2, *t1);
+ }
+
+ BOOST_AUTO_TEST_CASE(Format_sec_msec)
+ {
+ I2n::clock::Time t1 (42, 42);
+ I2n::clock::Time t2 ( 4, 242424242);
+ I2n::clock::Time t3 ( 0, 133713371);
+ I2n::clock::Time t4 ( 0, 0);
+
+ std::string s1 = t1.format_sec_msec ();
+ std::string s2 = t2.format_sec_msec ();
+ std::string s3 = t3.format_sec_msec ();
+ std::string s4 = t4.format_sec_msec ();
+
+ BOOST_CHECK_EQUAL("42s 0ms" , s1);
+ BOOST_CHECK_EQUAL( "4s 242ms", s2);
+ BOOST_CHECK_EQUAL( "0s 133ms", s3);
+ BOOST_CHECK_EQUAL( "0s 0ms" , s4);
+ }
+
+ BOOST_AUTO_TEST_CASE(Format_min_sec_msec)
+ {
+ I2n::clock::Time t1 (42*60 + 42, 42);
+ I2n::clock::Time t2 ( 4*60 + 42, 242424242);
+ I2n::clock::Time t3 ( 0*60 + 42, 133713371);
+ I2n::clock::Time t4 ( 0 + 0, 0);
+
+ std::string s1 = *t1.format_min_sec_msec ();
+ std::string s2 = *t2.format_min_sec_msec ();
+ std::string s3 = *t3.format_min_sec_msec ();
+ std::string s4 = *t4.format_min_sec_msec ();
+
+ BOOST_CHECK_EQUAL("42m42.000s", s1);
+ BOOST_CHECK_EQUAL( "4m42.242s", s2);
+ BOOST_CHECK_EQUAL( "0m42.133s", s3);
+ BOOST_CHECK_EQUAL( "0m0.000s", s4);
+ }
+
+ BOOST_AUTO_TEST_CASE(FormatISO8601_T)
+ {
+ I2n::clock::Time t (42, 42);
+ boost::optional<std::string> s = t.format_iso8601 (true, false, true, false);
+
+ BOOST_CHECK_EQUAL("00:00:42", *s);
+ }
+
+ BOOST_AUTO_TEST_CASE(FormatISO8601_DT)
+ {
+ I2n::clock::Time t (1541934671, 0);
+ boost::optional<std::string> s = t.format_iso8601 (true, true, true, false);
+
+ BOOST_CHECK_EQUAL("2018-11-11T11:11:11", *s);
+ }
+
+ BOOST_AUTO_TEST_CASE(FormatISO8601_DTZ)
+ {
+ I2n::clock::Time t (1541934671, 0);
+ boost::optional<std::string> s = t.format_iso8601 (true, true, true, true);
+
+ BOOST_CHECK_EQUAL("2018-11-11T11:11:11Z+0000", *s);
+ }
+
+ BOOST_AUTO_TEST_CASE(Format_make_nice_time)
+ {
+ I2n::clock::Time t (111111, 0);
+ boost::optional<std::string> s = t.make_nice_time ();
+
+ BOOST_CHECK_EQUAL("1 day, 06:51:51", *s);
+ }
+
+ BOOST_AUTO_TEST_CASE(Format_format_full_time)
+ {
+ I2n::clock::Time t (1541934671, 0);
+ /*
+ * brr, the old formatters use localtime without a way to opt out of
+ * it!
+ */
+ this->set_utc ();
+ boost::optional<std::string> s = t.format_full_time ();
+
+ BOOST_CHECK_EQUAL("11.11.2018 11:11", *s);
+ }
+
+ BOOST_AUTO_TEST_CASE(Format_format_date)
+ {
+ I2n::clock::Time t (1541934671, 0);
+ boost::optional<std::string> s = t.format_date ();
+
+ BOOST_CHECK_EQUAL("11.11.2018", *s);
+ }
+
+ BOOST_AUTO_TEST_CASE(FromString_iso8601_full)
+ {
+ const std::string in1 ("0001-01-01T00:00:00Z+0000");
+ const std::string in2 ("2018-11-11T11:11:11Z+0000");
+
+ this->set_utc ();
+
+ boost::optional<I2n::clock::Time> t1 = I2n::clock::time_of_iso8601 (in1);
+ boost::optional<I2n::clock::Time> t2 = I2n::clock::time_of_iso8601 (in2);
+
+# if LONG_BIT == 32
+ BOOST_CHECK(!t1);
+# else
+ BOOST_CHECK(t1);
+ BOOST_CHECK_EQUAL(*t1->format_iso8601 (), in1);
+# endif
+
+ BOOST_CHECK(t2);
+ BOOST_CHECK_EQUAL(*t2->format_iso8601 (), in2);
+ }
+
+ BOOST_AUTO_TEST_CASE(FromString_iso8601_full_negyear)
+ {
+ const std::string in1 ("-0001-01-01T00:00:00Z+0000");
+ const std::string in2 ("-2018-11-11T11:11:11Z+0000");
+
+ this->set_utc ();
+
+ boost::optional<I2n::clock::Time> t1 = I2n::clock::time_of_iso8601 (in1);
+ boost::optional<I2n::clock::Time> t2 = I2n::clock::time_of_iso8601 (in2);
+
+# if LONG_BIT == 32
+ BOOST_CHECK(!t1);
+ BOOST_CHECK(!t2);
+# else
+ BOOST_CHECK(t1);
+ BOOST_CHECK(t2);
+ BOOST_CHECK_EQUAL(*t1->format_iso8601 (), in1);
+ BOOST_CHECK_EQUAL(*t2->format_iso8601 (), in2);
+# endif
+ }
+
+ BOOST_AUTO_TEST_CASE(FromString_iso8601_partial)
+ {
+ const std::string in1 ("2018-11-11T11:11:11");
+ const std::string in2 ("2018-11-11");
+
+ this->set_utc ();
+
+ boost::optional<I2n::clock::Time> t1 =
+ I2n::clock::time_of_iso8601 (in1, true, true, false);
+ boost::optional<I2n::clock::Time> t2 =
+ I2n::clock::time_of_iso8601 (in2, true, false, false);
+
+ BOOST_CHECK(t1);
+ BOOST_CHECK(t2);
+ /*
+ * We test for the difference here which is zero if the number is
+ * correct but causes the difference from the expected value to be
+ * printed in case the test fails.
+ */
+ BOOST_CHECK_EQUAL(*t1->format_iso8601 (true, true, true, false), in1);
+ BOOST_CHECK_EQUAL(*t2->format_iso8601 (true, true, false, false), in2);
+ }
+
+ BOOST_AUTO_TEST_CASE(FromString_iso8601_32bit_time_t_err)
+ {
+ const std::string timeless ("11:11:11");
+ boost::optional<I2n::clock::Time> untimely = boost::none;
+
+ this->set_utc ();
+
+ untimely = I2n::clock::time_of_iso8601 (timeless, false, true, false);
+
+# if LONG_BIT == 32
+ BOOST_CHECK(!untimely);
+# else
+ BOOST_CHECK(untimely);
+ BOOST_CHECK_EQUAL(*untimely->format_iso8601 (true, false, true, false),
+ timeless);
+# endif
+ }
+
+ BOOST_AUTO_TEST_CASE(Ctor_32bit_time_t_err)
+ {
+ boost::optional<std::string> threw = boost::none;
+
+ struct tm tm;
+ memset (&tm, 0, sizeof (tm));
+
+ tm.tm_sec = 11;
+ tm.tm_min = 11;
+ tm.tm_hour = 11;
+ tm.tm_mday = 11;
+ tm.tm_mon = 10;
+ tm.tm_year = -789;
+ tm.tm_gmtoff = 0;
+
+ try {
+ I2n::clock::Time untimely (tm);
+ } catch (I2n::clock::conversion_error &exn) {
+ threw = std::string (exn);
+ }
+
+
+# if LONG_BIT == 32
+ BOOST_CHECK_EQUAL(*threw,
+ "errno=0 [mktime: from struct tm {Sun Nov 11 11:11:11 1111}]");
+# else
+ BOOST_CHECK(!threw);
+# endif
+ }
+
+ BOOST_AUTO_TEST_CASE(containers_list)
+ {
+ std::list<I2n::clock::Time> ts;
+
+ ts.push_back (I2n::clock::zero ());
+ ts.push_back (I2n::clock::zero ());
+
+ BOOST_CHECK_EQUAL(ts.size (), 2);
+ }
+
+ BOOST_AUTO_TEST_CASE(containers_vec)
+ {
+ std::vector<I2n::clock::Time> ts;
+
+ ts.push_back (I2n::clock::zero ());
+ ts.push_back (I2n::clock::zero ());
+
+ BOOST_CHECK_EQUAL(ts.size (), 2);
+ }
+
+ BOOST_AUTO_TEST_CASE(containers_set)
+ {
+ std::set<I2n::clock::Time> ts;
+
+ ts.insert (I2n::clock::zero ());
+ ts.insert (I2n::clock::Time (42, 2187));
+ ts.insert (I2n::clock::zero ());
+
+ BOOST_CHECK_EQUAL(ts.size (), 2);
+ }
+
+ BOOST_AUTO_TEST_CASE(containers_list_mean)
+ {
+ std::list<I2n::clock::Time> ts;
+
+ ts.push_back (I2n::clock::Time (42, 42));
+ ts.push_back (I2n::clock::Time (1337, 1337));
+
+ BOOST_CHECK_EQUAL(I2n::clock::mean (ts),
+ I2n::clock::Time (689, 500000689));
+ }
+
+ BOOST_AUTO_TEST_CASE(containers_list_mean_zero)
+ {
+ std::list<I2n::clock::Time> ts;
+
+ ts.push_back (I2n::clock::Time (0, 0));
+ ts.push_back (I2n::clock::Time (0, 0));
+ ts.push_back (I2n::clock::Time (0, 0));
+ ts.push_back (I2n::clock::Time (0, 0));
+
+ BOOST_CHECK_EQUAL(I2n::clock::mean (ts),
+ I2n::clock::zero ());
+ }
+
+ BOOST_AUTO_TEST_CASE(containers_list_mean_empty)
+ {
+ std::list<I2n::clock::Time> ts;
+
+ BOOST_CHECK_EQUAL(I2n::clock::mean (ts), I2n::clock::Time (0, 0));
+ }
+
+ BOOST_AUTO_TEST_CASE(containers_set_mean)
+ {
+ std::set<I2n::clock::Time> ts;
+
+ ts.insert (I2n::clock::Time (42));
+ ts.insert (I2n::clock::Time (1337));
+ ts.insert (I2n::clock::Time (2187));
+
+ BOOST_CHECK_EQUAL(I2n::clock::mean (ts),
+ I2n::clock::Time (1188, 666666666));
+ }
+
+ BOOST_AUTO_TEST_CASE(containers_set_median_empty)
+ {
+ std::set<I2n::clock::Time> ts;
+
+ BOOST_CHECK_EQUAL(I2n::clock::median (ts), I2n::clock::Time (0, 0));
+ }
+
+ BOOST_AUTO_TEST_CASE(containers_set_median_one)
+ {
+ std::set<I2n::clock::Time> ts;
+
+ ts.insert (I2n::clock::Time (42, 0));
+
+ BOOST_CHECK_EQUAL(I2n::clock::median (ts),
+ I2n::clock::Time (42, 0));
+ }
+
+ BOOST_AUTO_TEST_CASE(containers_set_median_multi)
+ {
+ std::set<I2n::clock::Time> ts;
+
+ ts.insert (I2n::clock::Time (42));
+ ts.insert (I2n::clock::Time (1337));
+ ts.insert (I2n::clock::Time (2187));
+
+ BOOST_CHECK_EQUAL(I2n::clock::median (ts), I2n::clock::Time (1337));
+ }
+
+ BOOST_AUTO_TEST_CASE(containers_vec_median_multi)
+ {
+ std::vector<I2n::clock::Time> ts;
+
+ ts.push_back (I2n::clock::Time (42));
+ ts.push_back (I2n::clock::Time (1337));
+ ts.push_back (I2n::clock::Time (2187));
+
+ BOOST_CHECK_EQUAL(I2n::clock::median (ts), I2n::clock::Time (1337));
+ }
+
+ BOOST_AUTO_TEST_CASE(containers_list_median_multi)
+ {
+ std::list<I2n::clock::Time> ts;
+
+ ts.push_back (I2n::clock::Time (42));
+ ts.push_back (I2n::clock::Time (1337));
+ ts.push_back (I2n::clock::Time (2187));
+ ts.push_back (I2n::clock::Time (0xdead));
+ ts.push_back (I2n::clock::Time (0xbeef));
+
+ BOOST_CHECK_EQUAL(I2n::clock::median (ts),
+ I2n::clock::Time (2187));
+ }
+
+ BOOST_AUTO_TEST_CASE(containers_list_median_multi_evensize)
+ {
+ std::list<I2n::clock::Time> ts;
+
+ ts.push_back (I2n::clock::Time (42));
+ ts.push_back (I2n::clock::Time (1337));
+ ts.push_back (I2n::clock::Time (2187));
+ ts.push_back (I2n::clock::Time (0xf00d));
+ ts.push_back (I2n::clock::Time (0xfeed));
+ ts.push_back (I2n::clock::Time (0xdeadf0e));
+
+ BOOST_CHECK_EQUAL(I2n::clock::median (ts), I2n::clock::Time (0xf00d));
+ }
+
+BOOST_AUTO_TEST_SUITE_END() /* [Clock] */
BOOST_AUTO_TEST_SUITE_END()