Fix output unit for Time::operator<<
[libi2ncommon] / src / timefunc.hxx
index 3f5810a..3cb34ad 100644 (file)
+/*
+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 &copy; 2001-2008 by Intra2net AG
- * @license commercial
- * @contact info@intra2net.com
- *
+ * @copyright Copyright &copy; 2001-2018 by Intra2net AG
  */
 
 #ifndef __TIMEFUNC_HXX
 #define __TIMEFUNC_HXX
 
-#include <string>
-#include <bitset>
+#include <climits>
+#include <errno.h>
 #include <list>
+#include <string>
+#include <cstdlib>
 
+#include <boost/optional.hpp>
 
-double prec_time(void);
+#include <week.hpp>
 
-int date_to_seconds(const std::string &date);
+#include "stringfunc.hxx"
 
-std::string make_nice_time(int seconds);
-std::string format_full_time(int seconds);
-void seconds_to_hour_minute(int seconds, int *hour, int *minute);
-std::string output_hour_minute(int hour, int minute, bool h_for_00=true);
+#if __cplusplus >= 201103
+# define CONSTEXPR constexpr
+# define NOEXCEPT  noexcept
+#else
+# define CONSTEXPR
+# define NOEXCEPT
+#endif
 
-inline std::string output_hour_minute_from_seconds(int seconds)
-{
-    int hour, minute;
-    seconds_to_hour_minute(seconds,&hour,&minute);
-    return output_hour_minute(hour,minute);
-}
+#define TIME_CONST_FACTOR_NANO (1000L * 1000 * 1000)
 
-std::string get_month_name(unsigned char month);
+double prec_time(void);
 
+time_t date_to_seconds(const std::string &date);
 
-/**
- * @brief represents some days of a week.
- *
- */
-class WEEK
+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)
 {
-    private:
-        std::bitset<7> days;
-
-    public:
-        enum WEEKDAY { SU=0, MO=1, TU=2, WE=3, TH=4, FR=5, SA=6 };
-
-        // throws out_of_range if illegal week
-        WEEK(const std::string& daystring);
-
-        WEEK(const std::bitset<7> &_days)
-            : days(_days)
-            { }
-
-        WEEK()
-            { }
-
-        void clear()
-            { days.reset(); }
-
-        operator std::bitset<7>() const
-            { return days; }
-
-        void set(WEEKDAY d, bool value=true)
-            { days[d]=value; }
-
-        bool all_set() const
-            { return (days.count()==7); }
-        bool none_set() const
-            { return days.none(); }
-
-        std::string get_daystring() const;
-        std::string get_displaystring() const;
-        std::string get_netfilterstring() const;
-
-        static std::string get_day_display(WEEKDAY day);
-        static std::string get_english_display(WEEKDAY day);
-
-        // some operators for convenience:
-
-        WEEK& operator&=(const WEEK& rhs)
-        {
-            days &= rhs.days;
-            return *this;
-        }
-
-        WEEK& operator|=(const WEEK& rhs)
-        {
-            days|= rhs.days;
-            return *this;
-        }
-
-        WEEK& operator^=(const WEEK& rhs)
-        {
-            days^= rhs.days;
-            return *this;
-        }
-
-        bool operator==(const WEEK& rhs)
-        {
-            return days == rhs.days;
-        }
-
-        bool operator!=(const WEEK& rhs)
-        {
-            return days != rhs.days;
-        }
-
-}; // eo class WEEK
-
+    return I2n::to_string (ts.tv_sec) + "s "
+         + I2n::to_string (ts.tv_nsec / 1000000) + "ms"
+         ;
+}
 
-/**
- * @brief delivers a week containing the days which are in both weeks.
- * @param lhs first week.
- * @param rhs second week.
- * @return week which has only those days which are in both weeks.
- */
-inline WEEK operator&(const WEEK& lhs, const WEEK& rhs)
-{
-    WEEK result(lhs);
-    return result &= rhs;
-} // eo operator&(const WEEK&,const WEEK&)
+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); }
 
-/**
- * @brief delivers a week containing the days which are at least in one of both weeks.
- * @param lhs first week.
- * @param rhs second week.
- * @return week which has only those days which are at least in one of both weeks.
- */
-inline WEEK operator|(const WEEK& lhs, const WEEK& rhs)
-{
-    WEEK result(lhs);
-    return result |= rhs;
-} // eo operator&(const WEEK&,const WEEK&)
 
+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);
 
-/**
- * @brief delivers a week containing the days which are in only one of both weeks.
- * @param lhs first week.
- * @param rhs second week.
- * @return week which has only those days which are in only one of both weeks.
- */
-inline WEEK operator^(const WEEK& lhs, const WEEK& rhs)
+inline std::string output_hour_minute_from_seconds(int seconds)
 {
-    WEEK result(lhs);
-    return result ^= rhs;
-} // eo operator&(const WEEK&,const WEEK&)
+    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.
@@ -158,12 +107,16 @@ class Interval
         Interval()
         : m_lower_bound(0)
         , m_upper_bound(0)
+        , m_weak_mark(0)
+        , m_changed(false)
         {
         } //
 
-        Interval( unsigned int start, unsigned int end )
+        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)
         {
         } //
 
@@ -184,6 +137,10 @@ class Interval
         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
         {
@@ -191,6 +148,12 @@ class Interval
         } // 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.
@@ -213,6 +176,9 @@ class Interval
         unsigned int m_lower_bound;
         unsigned int m_upper_bound;
 
+        int m_weak_mark;
+        bool m_changed;
+
 }; // eo Interval
 
 
@@ -266,8 +232,10 @@ class Intervals
         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) ; }
+        bool operator!=(const Intervals& other) const { return not (*this == other) ; }
 
         Intervals& operator+=(const Interval& other);
         Intervals& operator-=(const Interval& other);
@@ -282,6 +250,445 @@ class 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 &divide (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