X-Git-Url: http://developer.intra2net.com/git/?a=blobdiff_plain;f=src%2Ftimefunc.hxx;fp=src%2Ftimefunc.hxx;h=3cb34ad5fdf3367433b0167e74ba8f92cf632a2e;hb=c443fce815c32e8aa716bcff6022e79b4f12a0e7;hp=18b03148858b25cbac8e7c1cd0fff5e3c5bbc16f;hpb=4cefaf511d9ed79ad13ab59c46bd0ddc60e758f6;p=libi2ncommon diff --git a/src/timefunc.hxx b/src/timefunc.hxx index 18b0314..3cb34ad 100644 --- a/src/timefunc.hxx +++ b/src/timefunc.hxx @@ -20,17 +20,34 @@ on this file might be covered by the GNU General Public License. /** @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 +#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); @@ -38,6 +55,36 @@ 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); @@ -213,6 +260,435 @@ 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