/* The software in this package is distributed under the GNU General Public License version 2 (with a special exception described below). A copy of GNU General Public License (GPL) is included in this distribution, in the file COPYING.GPL. As a special exception, if other files instantiate templates or use macros or inline functions from this file, or you compile this file and link it with other works to produce a work based on this file, this file does not by itself cause the resulting work to be covered by the GNU General Public License. However the source code for this file must still be made available in accordance with section (3) of the GNU General Public License. This exception does not invalidate any other reasons why a work based on this file might be covered by the GNU General Public License. */ /** @file * @brief time related functions. * * @copyright Copyright © 2001-2008 by Intra2net AG * */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include using namespace std; double prec_time(void) { struct timespec ts = { 0 }; double ret = 0.0; if (clock_gettime(CLOCK_REALTIME_COARSE, &ts) == -1) { /* Something’s wrong on the kernel end! */ return ret; } ret = static_cast(ts.tv_sec); ret += static_cast(ts.tv_nsec) / static_cast(TIME_CONST_FACTOR_NANO); return ret; } // converts ISO-DATE: 2003-06-13 time_t date_to_seconds(const std::string &date) { time_t rtn = 0; int year = -1, month = -1, day = -1; string::size_type pos = date.find("-"); if (pos == string::npos) return rtn; istringstream in(string(date,0,pos)); in >> year; year -= 1900; string dstr(date, pos+1); if ((pos = dstr.find("-")) == string::npos) return rtn; in.clear(); in.str(string(dstr, 0, pos)); in >> month; month -= 1; in.clear(); in.str(string(dstr, pos+1)); in >> day; if (year < 0 || month == -1 || day == -1) return rtn; struct tm tm_struct; memset(&tm_struct, 0, sizeof(struct tm)); tm_struct.tm_year = year; tm_struct.tm_mon = month; tm_struct.tm_mday = day; tm_struct.tm_isdst = -1; rtn = mktime (&tm_struct); return rtn; } string make_nice_time(int seconds) { ostringstream out; int days=seconds/86400; seconds%=86400; int hours,minutes; split_daysec(seconds,&hours,&minutes,&seconds); if (days>0) out << days << " " << i18n_plural("day", "days", days) << ", "; out << setfill('0'); out << setw(2) << hours << ':' << setw(2) << minutes << ':' << setw(2) << seconds; return out.str(); } string format_full_time(time_t seconds) { char buf[50]; memset (buf, 0, 50); struct tm ta; if (localtime_r((time_t *)&seconds, &ta) == NULL) memset (&ta, 0, sizeof(struct tm)); strftime (buf, 49, "%d.%m.%Y %H:%M", &ta); return string(buf); } string format_date(time_t seconds) { char buf[50]; memset (buf, 0, 50); struct tm ta; if (localtime_r((time_t *)&seconds, &ta) == NULL) memset (&ta, 0, sizeof(struct tm)); strftime (buf, 49, "%d.%m.%Y", &ta); return string(buf); } void seconds_to_hour_minute(int seconds, int *hour, int *minute) { if (hour != NULL) { *hour = 0; while (seconds >= 3600) { seconds-=3600; (*hour)++; } } if (minute != NULL) { *minute = 0; while (seconds >= 60) { seconds-=60; (*minute)++; } } } /** * Split seconds into hours, minutes and seconds * @param [in] daysec Seconds since start of day * @param [out] outhours hours * @param [out] outminutes minutes * @param [out] outseconds seconds */ void split_daysec(int daysec, int *outhours, int *outminutes, int *outseconds) { int hours=daysec/3600; daysec%=3600; int minutes=daysec/60; daysec%=60; if (outhours) *outhours=hours; if (outminutes) *outminutes=minutes; if (outseconds) *outseconds=daysec; } std::string output_hour_minute(int hour, int minute, bool h_for_00, int seconds) { ostringstream out; if (hour >= 0 && hour < 10) out << '0'; out << hour; if (!h_for_00 || minute != 0 || seconds > 0) { out << ':'; if (minute >= 0 && minute < 10) out << '0'; out << minute; } else out << 'h'; if (seconds > 0) { out << ':'; if (seconds > 0 && seconds < 10) out << '0'; out << seconds; } return out.str(); } string get_month_name(unsigned char month) { string rtn; switch(month) { case 1: rtn = i18n("January"); break; case 2: rtn = i18n("February"); break; case 3: rtn = i18n("March"); break; case 4: rtn = i18n("April"); break; case 5: rtn = i18n("May"); break; case 6: rtn = i18n("June"); break; case 7: rtn = i18n("July"); break; case 8: rtn = i18n("August"); break; case 9: rtn = i18n("September"); break; case 10: rtn = i18n("October"); break; case 11: rtn = i18n("November"); break; case 12: rtn = i18n("December"); break; default: { ostringstream out; out << i18n("Illegal month:") << " " << month; rtn = out.str(); } } return rtn; } /* ** implementaion of Interval */ /** * @brief clears the interval (make it empty). */ void Interval::clear() { m_lower_bound = m_upper_bound = 0; } // eo Interval::clear() /** * @brief tests if there is some overlapping with another interval * @param other the other interval * @return @a true if the two intervals have a non empty intersection. */ bool Interval::intersects(const Interval& other) const { return // // other start within this: (other.m_lower_bound >= m_lower_bound and other.m_lower_bound < m_upper_bound ) // // other end within this: or (other.m_upper_bound > m_lower_bound and other.m_upper_bound <= m_upper_bound ) // // other contains this or (other.m_lower_bound <= m_lower_bound and other.m_upper_bound >= m_upper_bound ) ; } // eo Interval::intersects(const Interval&) /** * @brief tests if the current interval (fully) contains another one. * @param other the other interval. * @return @a true if the current interval fully contains the other interval. */ bool Interval::contains(const Interval& other) const { return (other.m_lower_bound >= m_lower_bound) and (other.m_upper_bound <= m_upper_bound) ; } // eo Interval::contains(const Interval& other) const /* ** implementation of Intervals: */ Intervals::Intervals() { } // eo Intervals::Intervals void Intervals::clear() { m_intervals.clear(); } // eo Intervals::clear() /** * @brief tests if one of the intervals of the list intersects with the given interval. * @param other the interval to check for intersection. * @return @a true if there is an intersection. */ bool Intervals::intersects(const Interval& other) const { for(const_iterator it= begin(); it != end(); ++it) { if ( it->intersects(other) ) { return true; } } return false; } // eo Intervals::intersects(const Interval&) const /** * @brief tests if we have at least one intersection with another Intervals instance. * @param other the other instance. * @return @a true if there is an intersection. */ bool Intervals::intersects(const Intervals& other) const { for(const_iterator it= begin(); it != end(); ++it) { if ( other.intersects( *it ) ) { return true; } } return false; } // eo Intervals::intersects(const Intervals&) const /** * @brief adds a new interval to the list. * @param new_frame the new interval. * * Adds the interval to the list and joins overlapping intervals. * * @internal complexity O(n). */ void Intervals::add(const Interval& new_frame) { if (not new_frame.is_valid() or new_frame.empty()) { // well... we will not insert invalid or empty frames! return; } for (IntervalList::iterator it= m_intervals.begin(); it != m_intervals.end(); ++it) { Interval& current_frame = *it; if ( new_frame.m_lower_bound > current_frame.m_upper_bound ) { // new_frame begins later than current end; go on: continue; } // at this point: the begin of the new frame is less then the current end. // now let's determine how we can insert the new frame: if ( new_frame.m_upper_bound < current_frame.m_lower_bound ) { // new disjoint frame; insert it before the current frame: m_intervals.insert( it, new_frame ); // and we are done. return; } // at this point: the end of the new frame is >= current begin. if ( new_frame.m_upper_bound <= current_frame.m_upper_bound ) { // the end of the new frame is within our current frame; we need to combine if (new_frame.m_lower_bound < current_frame.m_lower_bound) { // the new interval starts earlier; we need to adjust our current frame: current_frame.m_lower_bound = new_frame.m_lower_bound; current_frame.m_changed = true; } // NOTE no "else" part needed since in that case our current frame already // contains the new one! // we are done: return; } // at this point: end of new frame > end of current frame // so we need to extend the current frame; at least the end. // But we need to deal with intersects of following frames... *sigh* // first the simple part: let's see if we need to move the start: if ( new_frame.m_lower_bound < current_frame.m_lower_bound) { // yes, we need to move the start: current_frame.m_lower_bound = new_frame.m_lower_bound; current_frame.m_changed= true; } // now let's extend the end: current_frame.m_upper_bound = new_frame.m_upper_bound; current_frame.m_changed = true; // well... let's walk through the following frames; looking for more joins...: IntervalList::iterator it2 = it; while( ++(it2=it) != m_intervals.end() and current_frame.m_upper_bound >= it2->m_lower_bound ) { Interval next_frame= *it2; if ( current_frame.m_upper_bound < next_frame.m_upper_bound ) { // in this case our end is within the next frame. // adjust our end. current_frame.m_upper_bound = next_frame.m_upper_bound; } // and remove the next frame since the current frame contains it (now): m_intervals.erase(it2); } // we are done! return; } // at this point: new frame starts later than the last frame ends // append the new frame: m_intervals.push_back( new_frame ); } // eo Intervals::add(const Interval&) /** * @brief subtracts a time interval from the list. * @param del_frame the time interval to subtract. * * removes the time interval from the list; cut off parts from or remove existing * intervals if they overlap. * * @internal complexity O(n). */ void Intervals::sub(const Interval& del_frame) { if (not del_frame.is_valid() or del_frame.empty() ) { return; } for (IntervalList::iterator it= m_intervals.begin(); it != m_intervals.end(); ) { Interval& current_frame = *it; if ( del_frame.m_lower_bound >= current_frame.m_upper_bound ) { // del_frame begins later than current end; go on: ++it; continue; } // at this point: the begin of the del frame is less then the current end. if ( del_frame.m_upper_bound < current_frame.m_lower_bound ) { // end is before our start; nothing to do. return; } // at this point: the end of the del frame is >= current begin. if ( del_frame.m_upper_bound < current_frame.m_upper_bound ) { // del frame end point is within our interval. if ( del_frame.m_lower_bound > current_frame.m_lower_bound) { // the del frame is within our interval... we need to split: m_intervals.insert(it, Interval( current_frame.m_lower_bound, del_frame.m_lower_bound ) ); } // adjust start of current frame: if (current_frame.m_lower_bound < del_frame.m_upper_bound) { current_frame.m_lower_bound= del_frame.m_upper_bound; current_frame.m_changed= true; } // and we are done! return; } // at this point the end of the del frame is >= current end if ( del_frame.m_lower_bound > current_frame.m_lower_bound ) { // a part of the current interval needs to be preserved.. // move the end. current_frame.m_upper_bound= del_frame.m_lower_bound; current_frame.m_changed= true; // and continue with the next interval: ++it; continue; } // at this point; the whole frame needs to be deleted.. if ( it == m_intervals.begin()) { m_intervals.erase(it); it= m_intervals.begin(); } else { IntervalList::iterator it2= it++; m_intervals.erase(it2); } } } // eo Intervals::sub(const Interval&) /** * @brief returns if we contain an interval. * @param other the interval to check. * @return @a true if we cover the given interval, too. */ bool Intervals::contains(const Interval& other) const { for(const_iterator it= begin(); it != end(); ++it) { if ( it->contains( other )) { return true; } } return false; } // eo Intervals::contains(const Interval&) const /** * @brief returns if we contain an exact interval. * @param other the interval to check. * @return @a true if we axactly contains the given interval. * * @note thsi differs from contain in the way, that we return only @a true * iff we have the given interval in our list; not only cover it. */ bool Intervals::contains_exact(const Interval& other) const { for(const_iterator it= begin(); it != end(); ++it) { if ( *it == other) { return true; } } return false; } // eo Intervals::contains_exact(const Interval&)const /** * @brief returns if we contain another interval combination. * @param other the intervals to check. * @return @a true if we cover the given intervals, too. * * @internal we rely on the fact that the lists are sorted and contain * disjoint intervals. * * So this method has a complexity of O(n). */ bool Intervals::contains(const Intervals& other) const { const_iterator my_it= begin(); const_iterator other_it= other.begin(); while( my_it != end() and other_it!= other.end() ) { // seek the first interval which contains the lower bound of the current other interval while (my_it != end() and my_it->m_lower_bound > other_it->m_lower_bound and other_it->m_lower_bound >= my_it->m_upper_bound ) { ++my_it; } if (my_it == end()) { break; } if (not my_it->contains( *other_it )) { // if we don't contain the current other; we're done: return false; } //else check the next other interval: ++other_it; } return (other_it == other.end()); } // eo Intervals::contains(const Intervals&) const /** * @brief combines to interval combinates for equality * @param other the other instance. * @return @a true if the other is equal to the current. * * @internal since the lists are sorted, we compare the interval lists. * Thus we have a complexity of O(n). */ bool Intervals::operator==(const Intervals& other) const { // since we keep sorted lists: just compare the lists :-) return m_intervals == other.m_intervals; } // eo Intervals::operator==(const Intervals&) Intervals& Intervals::operator+=(const Interval& other) { add(other); return *this; } // eo operator+=(const Interval&) Intervals& Intervals::operator-=(const Interval& other) { sub(other); return *this; } // eo operator-=(const Interval&) /** * @brief adds the intervals of a second instance to us. * @param other the other instance. * @return self reference (allow chaining). * * @internal since we do simple loops over the other and our intervals * we have a complexity of O(n^2). * * @todo optimize if complexity becomes a problem. */ Intervals& Intervals::operator+=(const Intervals& other) { for(const_iterator it= other.begin(); it != other.end(); ++it) { add( *it ); } return *this; } // eo operator+=(const Intervals&) /** * @brief subtracts the intervals of a second instance from us. * @param other the other instance. * @return self reference (allow chaining). * * @internal since we do simple loops over the other and our intervals * we have a complexity of O(n^2). * * @todo optimize if complexity becomes a problem. */ Intervals& Intervals::operator-=(const Intervals& other) { if (&other == this) { m_intervals.clear(); } else { for(const_iterator it= other.begin(); it != other.end(); ++it) { sub( *it ); } } return *this; } // eo operator-=(const Intervals&) /* ** clock funcs: */ /** * @brief fetches the value from the monotonic clock source. * @param[out] seconds the seconds. * @param[out] nano_seconds the nano seconds. * @return @a true if the clock was successfully read. */ bool monotonic_clock_gettime(long int& seconds, long int& nano_seconds) { struct timespec tp[1]; int res= clock_gettime (CLOCK_MONOTONIC, tp); if (0 == res) { seconds= tp->tv_sec; nano_seconds= tp->tv_nsec; } return (res==0); } // eo monotonic_clock_gettime(long int&,long int&) /** * @brief fetches the value from the monotonic clock source. * @return the time since system start in nanoseconds, 0 if read was unsuccessful */ long long monotonic_clock_gettime_nano() { long int seconds; long int nano_seconds; long long nano=0; if (monotonic_clock_gettime(seconds,nano_seconds)) { nano=seconds; nano*=1000000000LL; nano+=nano_seconds; } return nano; } /** * @brief fetches the value from the monotonic clock source. * @param[out] seconds the seconds. * @param[out] nano_seconds the nano seconds. * @return @a true if the clock was successfully read. */ bool realtime_clock_gettime(long int& seconds, long int& nano_seconds) { struct timespec tp[1]; int res= clock_gettime(CLOCK_REALTIME, tp); if (0 == res) { seconds= tp->tv_sec; nano_seconds= tp->tv_nsec; } return (res==0); } // 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 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 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 For internal use only. 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(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 (this->value.tv_sec ); int64_t nsec = static_cast (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 (div.quot); this->value.tv_nsec = static_cast (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 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 (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