xref: /src/contrib/kyua/utils/datetime.cpp (revision 6b13d60bf49ee40626d7e3a5d5a80519f0067307)
108334c51SBrooks Davis // Copyright 2010 The Kyua Authors.
208334c51SBrooks Davis // All rights reserved.
308334c51SBrooks Davis //
408334c51SBrooks Davis // Redistribution and use in source and binary forms, with or without
508334c51SBrooks Davis // modification, are permitted provided that the following conditions are
608334c51SBrooks Davis // met:
708334c51SBrooks Davis //
808334c51SBrooks Davis // * Redistributions of source code must retain the above copyright
908334c51SBrooks Davis //   notice, this list of conditions and the following disclaimer.
1008334c51SBrooks Davis // * Redistributions in binary form must reproduce the above copyright
1108334c51SBrooks Davis //   notice, this list of conditions and the following disclaimer in the
1208334c51SBrooks Davis //   documentation and/or other materials provided with the distribution.
1308334c51SBrooks Davis // * Neither the name of Google Inc. nor the names of its contributors
1408334c51SBrooks Davis //   may be used to endorse or promote products derived from this software
1508334c51SBrooks Davis //   without specific prior written permission.
1608334c51SBrooks Davis //
1708334c51SBrooks Davis // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
1808334c51SBrooks Davis // "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
1908334c51SBrooks Davis // LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
2008334c51SBrooks Davis // A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
2108334c51SBrooks Davis // OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
2208334c51SBrooks Davis // SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
2308334c51SBrooks Davis // LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
2408334c51SBrooks Davis // DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
2508334c51SBrooks Davis // THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
2608334c51SBrooks Davis // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
2708334c51SBrooks Davis // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
2808334c51SBrooks Davis 
2908334c51SBrooks Davis #include "utils/datetime.hpp"
3008334c51SBrooks Davis 
3108334c51SBrooks Davis extern "C" {
3208334c51SBrooks Davis #include <sys/time.h>
3308334c51SBrooks Davis 
3408334c51SBrooks Davis #include <time.h>
3508334c51SBrooks Davis }
3608334c51SBrooks Davis 
3708334c51SBrooks Davis #include <stdexcept>
3808334c51SBrooks Davis 
3908334c51SBrooks Davis #include "utils/format/macros.hpp"
4008334c51SBrooks Davis #include "utils/optional.ipp"
4108334c51SBrooks Davis #include "utils/noncopyable.hpp"
4208334c51SBrooks Davis #include "utils/sanity.hpp"
4308334c51SBrooks Davis 
4408334c51SBrooks Davis namespace datetime = utils::datetime;
4508334c51SBrooks Davis 
4608334c51SBrooks Davis using utils::none;
4708334c51SBrooks Davis using utils::optional;
4808334c51SBrooks Davis 
4908334c51SBrooks Davis 
5008334c51SBrooks Davis namespace {
5108334c51SBrooks Davis 
5208334c51SBrooks Davis 
5308334c51SBrooks Davis /// Fake value for the current time.
5408334c51SBrooks Davis static optional< datetime::timestamp > mock_now = none;
5508334c51SBrooks Davis 
5608334c51SBrooks Davis 
5708334c51SBrooks Davis }  // anonymous namespace
5808334c51SBrooks Davis 
5908334c51SBrooks Davis 
6008334c51SBrooks Davis /// Creates a zero time delta.
delta(void)6108334c51SBrooks Davis datetime::delta::delta(void) :
6208334c51SBrooks Davis     seconds(0),
6308334c51SBrooks Davis     useconds(0)
6408334c51SBrooks Davis {
6508334c51SBrooks Davis }
6608334c51SBrooks Davis 
6708334c51SBrooks Davis 
6808334c51SBrooks Davis /// Creates a time delta.
6908334c51SBrooks Davis ///
7008334c51SBrooks Davis /// \param seconds_ The seconds in the delta.
7108334c51SBrooks Davis /// \param useconds_ The microseconds in the delta.
7208334c51SBrooks Davis ///
7308334c51SBrooks Davis /// \throw std::runtime_error If the input delta is negative.
delta(const int64_t seconds_,const unsigned long useconds_)7408334c51SBrooks Davis datetime::delta::delta(const int64_t seconds_,
7508334c51SBrooks Davis                        const unsigned long useconds_) :
7608334c51SBrooks Davis     seconds(seconds_),
7708334c51SBrooks Davis     useconds(useconds_)
7808334c51SBrooks Davis {
7908334c51SBrooks Davis     if (seconds_ < 0) {
8008334c51SBrooks Davis         throw std::runtime_error(F("Negative deltas are not supported by the "
8108334c51SBrooks Davis                                    "datetime::delta class; got: %s") % (*this));
8208334c51SBrooks Davis     }
8308334c51SBrooks Davis }
8408334c51SBrooks Davis 
8508334c51SBrooks Davis 
8608334c51SBrooks Davis /// Converts a time expressed in microseconds to a delta.
8708334c51SBrooks Davis ///
8808334c51SBrooks Davis /// \param useconds The amount of microseconds representing the delta.
8908334c51SBrooks Davis ///
9008334c51SBrooks Davis /// \return A new delta object.
9108334c51SBrooks Davis ///
9208334c51SBrooks Davis /// \throw std::runtime_error If the input delta is negative.
9308334c51SBrooks Davis datetime::delta
from_microseconds(const int64_t useconds)9408334c51SBrooks Davis datetime::delta::from_microseconds(const int64_t useconds)
9508334c51SBrooks Davis {
9608334c51SBrooks Davis     if (useconds < 0) {
9708334c51SBrooks Davis         throw std::runtime_error(F("Negative deltas are not supported by the "
9808334c51SBrooks Davis                                    "datetime::delta class; got: %sus") %
9908334c51SBrooks Davis                                  useconds);
10008334c51SBrooks Davis     }
10108334c51SBrooks Davis 
10208334c51SBrooks Davis     return delta(useconds / 1000000, useconds % 1000000);
10308334c51SBrooks Davis }
10408334c51SBrooks Davis 
10508334c51SBrooks Davis 
10608334c51SBrooks Davis /// Convers the delta to a flat representation expressed in microseconds.
10708334c51SBrooks Davis ///
10808334c51SBrooks Davis /// \return The amount of microseconds that corresponds to this delta.
10908334c51SBrooks Davis int64_t
to_microseconds(void) const11008334c51SBrooks Davis datetime::delta::to_microseconds(void) const
11108334c51SBrooks Davis {
11208334c51SBrooks Davis     return seconds * 1000000 + useconds;
11308334c51SBrooks Davis }
11408334c51SBrooks Davis 
11508334c51SBrooks Davis 
11608334c51SBrooks Davis /// Checks if two time deltas are equal.
11708334c51SBrooks Davis ///
11808334c51SBrooks Davis /// \param other The object to compare to.
11908334c51SBrooks Davis ///
12008334c51SBrooks Davis /// \return True if the two time deltas are equals; false otherwise.
12108334c51SBrooks Davis bool
operator ==(const datetime::delta & other) const12208334c51SBrooks Davis datetime::delta::operator==(const datetime::delta& other) const
12308334c51SBrooks Davis {
12408334c51SBrooks Davis     return seconds == other.seconds && useconds == other.useconds;
12508334c51SBrooks Davis }
12608334c51SBrooks Davis 
12708334c51SBrooks Davis 
12808334c51SBrooks Davis /// Checks if two time deltas are different.
12908334c51SBrooks Davis ///
13008334c51SBrooks Davis /// \param other The object to compare to.
13108334c51SBrooks Davis ///
13208334c51SBrooks Davis /// \return True if the two time deltas are different; false otherwise.
13308334c51SBrooks Davis bool
operator !=(const datetime::delta & other) const13408334c51SBrooks Davis datetime::delta::operator!=(const datetime::delta& other) const
13508334c51SBrooks Davis {
13608334c51SBrooks Davis     return !(*this == other);
13708334c51SBrooks Davis }
13808334c51SBrooks Davis 
13908334c51SBrooks Davis 
14008334c51SBrooks Davis /// Checks if this time delta is shorter than another one.
14108334c51SBrooks Davis ///
14208334c51SBrooks Davis /// \param other The object to compare to.
14308334c51SBrooks Davis ///
14408334c51SBrooks Davis /// \return True if this time delta is shorter than other; false otherwise.
14508334c51SBrooks Davis bool
operator <(const datetime::delta & other) const14608334c51SBrooks Davis datetime::delta::operator<(const datetime::delta& other) const
14708334c51SBrooks Davis {
14808334c51SBrooks Davis     return seconds < other.seconds ||
14908334c51SBrooks Davis         (seconds == other.seconds && useconds < other.useconds);
15008334c51SBrooks Davis }
15108334c51SBrooks Davis 
15208334c51SBrooks Davis 
15308334c51SBrooks Davis /// Checks if this time delta is shorter than or equal to another one.
15408334c51SBrooks Davis ///
15508334c51SBrooks Davis /// \param other The object to compare to.
15608334c51SBrooks Davis ///
15708334c51SBrooks Davis /// \return True if this time delta is shorter than or equal to; false
15808334c51SBrooks Davis /// otherwise.
15908334c51SBrooks Davis bool
operator <=(const datetime::delta & other) const16008334c51SBrooks Davis datetime::delta::operator<=(const datetime::delta& other) const
16108334c51SBrooks Davis {
16208334c51SBrooks Davis     return (*this) < other || (*this) == other;
16308334c51SBrooks Davis }
16408334c51SBrooks Davis 
16508334c51SBrooks Davis 
16608334c51SBrooks Davis /// Checks if this time delta is larger than another one.
16708334c51SBrooks Davis ///
16808334c51SBrooks Davis /// \param other The object to compare to.
16908334c51SBrooks Davis ///
17008334c51SBrooks Davis /// \return True if this time delta is larger than other; false otherwise.
17108334c51SBrooks Davis bool
operator >(const datetime::delta & other) const17208334c51SBrooks Davis datetime::delta::operator>(const datetime::delta& other) const
17308334c51SBrooks Davis {
17408334c51SBrooks Davis     return seconds > other.seconds ||
17508334c51SBrooks Davis         (seconds == other.seconds && useconds > other.useconds);
17608334c51SBrooks Davis }
17708334c51SBrooks Davis 
17808334c51SBrooks Davis 
17908334c51SBrooks Davis /// Checks if this time delta is larger than or equal to another one.
18008334c51SBrooks Davis ///
18108334c51SBrooks Davis /// \param other The object to compare to.
18208334c51SBrooks Davis ///
18308334c51SBrooks Davis /// \return True if this time delta is larger than or equal to; false
18408334c51SBrooks Davis /// otherwise.
18508334c51SBrooks Davis bool
operator >=(const datetime::delta & other) const18608334c51SBrooks Davis datetime::delta::operator>=(const datetime::delta& other) const
18708334c51SBrooks Davis {
18808334c51SBrooks Davis     return (*this) > other || (*this) == other;
18908334c51SBrooks Davis }
19008334c51SBrooks Davis 
19108334c51SBrooks Davis 
19208334c51SBrooks Davis /// Adds a time delta to this one.
19308334c51SBrooks Davis ///
19408334c51SBrooks Davis /// \param other The time delta to add.
19508334c51SBrooks Davis ///
19608334c51SBrooks Davis /// \return The addition of this time delta with the other time delta.
19708334c51SBrooks Davis datetime::delta
operator +(const datetime::delta & other) const19808334c51SBrooks Davis datetime::delta::operator+(const datetime::delta& other) const
19908334c51SBrooks Davis {
20008334c51SBrooks Davis     return delta::from_microseconds(to_microseconds() +
20108334c51SBrooks Davis                                     other.to_microseconds());
20208334c51SBrooks Davis }
20308334c51SBrooks Davis 
20408334c51SBrooks Davis 
20508334c51SBrooks Davis /// Adds a time delta to this one and updates this with the result.
20608334c51SBrooks Davis ///
20708334c51SBrooks Davis /// \param other The time delta to add.
20808334c51SBrooks Davis ///
20908334c51SBrooks Davis /// \return The addition of this time delta with the other time delta.
21008334c51SBrooks Davis datetime::delta&
operator +=(const datetime::delta & other)21108334c51SBrooks Davis datetime::delta::operator+=(const datetime::delta& other)
21208334c51SBrooks Davis {
21308334c51SBrooks Davis     *this = *this + other;
21408334c51SBrooks Davis     return *this;
21508334c51SBrooks Davis }
21608334c51SBrooks Davis 
21708334c51SBrooks Davis 
21808334c51SBrooks Davis /// Scales this delta by a positive integral factor.
21908334c51SBrooks Davis ///
22008334c51SBrooks Davis /// \param factor The scaling factor.
22108334c51SBrooks Davis ///
22208334c51SBrooks Davis /// \return The scaled delta.
22308334c51SBrooks Davis datetime::delta
operator *(const std::size_t factor) const22408334c51SBrooks Davis datetime::delta::operator*(const std::size_t factor) const
22508334c51SBrooks Davis {
22608334c51SBrooks Davis     return delta::from_microseconds(to_microseconds() * factor);
22708334c51SBrooks Davis }
22808334c51SBrooks Davis 
22908334c51SBrooks Davis 
23008334c51SBrooks Davis /// Scales this delta by and updates this delta with the result.
23108334c51SBrooks Davis ///
23208334c51SBrooks Davis /// \param factor The scaling factor.
23308334c51SBrooks Davis ///
23408334c51SBrooks Davis /// \return The scaled delta as a reference to the input object.
23508334c51SBrooks Davis datetime::delta&
operator *=(const std::size_t factor)23608334c51SBrooks Davis datetime::delta::operator*=(const std::size_t factor)
23708334c51SBrooks Davis {
23808334c51SBrooks Davis     *this = *this * factor;
23908334c51SBrooks Davis     return *this;
24008334c51SBrooks Davis }
24108334c51SBrooks Davis 
24208334c51SBrooks Davis 
24308334c51SBrooks Davis /// Injects the object into a stream.
24408334c51SBrooks Davis ///
24508334c51SBrooks Davis /// \param output The stream into which to inject the object.
24608334c51SBrooks Davis /// \param object The object to format.
24708334c51SBrooks Davis ///
24808334c51SBrooks Davis /// \return The output stream.
24908334c51SBrooks Davis std::ostream&
operator <<(std::ostream & output,const delta & object)25008334c51SBrooks Davis datetime::operator<<(std::ostream& output, const delta& object)
25108334c51SBrooks Davis {
25208334c51SBrooks Davis     return (output << object.to_microseconds() << "us");
25308334c51SBrooks Davis }
25408334c51SBrooks Davis 
25508334c51SBrooks Davis 
25608334c51SBrooks Davis namespace utils {
25708334c51SBrooks Davis namespace datetime {
25808334c51SBrooks Davis 
25908334c51SBrooks Davis 
26008334c51SBrooks Davis /// Internal representation for datetime::timestamp.
26108334c51SBrooks Davis struct timestamp::impl : utils::noncopyable {
26208334c51SBrooks Davis     /// The raw timestamp as provided by libc.
26308334c51SBrooks Davis     ::timeval data;
26408334c51SBrooks Davis 
26508334c51SBrooks Davis     /// Constructs an impl object from initialized data.
26608334c51SBrooks Davis     ///
26708334c51SBrooks Davis     /// \param data_ The raw timestamp to use.
implutils::datetime::timestamp::impl26808334c51SBrooks Davis     impl(const ::timeval& data_) : data(data_)
26908334c51SBrooks Davis     {
27008334c51SBrooks Davis     }
27108334c51SBrooks Davis };
27208334c51SBrooks Davis 
27308334c51SBrooks Davis 
27408334c51SBrooks Davis }  // namespace datetime
27508334c51SBrooks Davis }  // namespace utils
27608334c51SBrooks Davis 
27708334c51SBrooks Davis 
27808334c51SBrooks Davis /// Constructs a new timestamp.
27908334c51SBrooks Davis ///
28008334c51SBrooks Davis /// \param pimpl_ An existing impl representation.
timestamp(std::shared_ptr<impl> pimpl_)28108334c51SBrooks Davis datetime::timestamp::timestamp(std::shared_ptr< impl > pimpl_) :
28208334c51SBrooks Davis     _pimpl(pimpl_)
28308334c51SBrooks Davis {
28408334c51SBrooks Davis }
28508334c51SBrooks Davis 
28608334c51SBrooks Davis 
28708334c51SBrooks Davis /// Constructs a timestamp from the amount of microseconds since the epoch.
28808334c51SBrooks Davis ///
28908334c51SBrooks Davis /// \param value Microseconds since the epoch in UTC.  Must be positive.
29008334c51SBrooks Davis ///
29108334c51SBrooks Davis /// \return A new timestamp.
29208334c51SBrooks Davis datetime::timestamp
from_microseconds(const int64_t value)29308334c51SBrooks Davis datetime::timestamp::from_microseconds(const int64_t value)
29408334c51SBrooks Davis {
29508334c51SBrooks Davis     PRE(value >= 0);
29608334c51SBrooks Davis     ::timeval data;
29708334c51SBrooks Davis     data.tv_sec = static_cast< time_t >(value / 1000000);
29808334c51SBrooks Davis     data.tv_usec = static_cast< suseconds_t >(value % 1000000);
29908334c51SBrooks Davis     return timestamp(std::shared_ptr< impl >(new impl(data)));
30008334c51SBrooks Davis }
30108334c51SBrooks Davis 
30208334c51SBrooks Davis 
30308334c51SBrooks Davis /// Constructs a timestamp based on user-friendly values.
30408334c51SBrooks Davis ///
30508334c51SBrooks Davis /// \param year The year in the [1900,inf) range.
30608334c51SBrooks Davis /// \param month The month in the [1,12] range.
30708334c51SBrooks Davis /// \param day The day in the [1,30] range.
30808334c51SBrooks Davis /// \param hour The hour in the [0,23] range.
30908334c51SBrooks Davis /// \param minute The minute in the [0,59] range.
31008334c51SBrooks Davis /// \param second The second in the [0,60] range.  Yes, that is 60, which can be
31108334c51SBrooks Davis ///     the case on leap seconds.
31208334c51SBrooks Davis /// \param microsecond The microsecond in the [0,999999] range.
31308334c51SBrooks Davis ///
31408334c51SBrooks Davis /// \return A new timestamp.
31508334c51SBrooks Davis datetime::timestamp
from_values(const int year,const int month,const int day,const int hour,const int minute,const int second,const int microsecond)31608334c51SBrooks Davis datetime::timestamp::from_values(const int year, const int month,
31708334c51SBrooks Davis                                  const int day, const int hour,
31808334c51SBrooks Davis                                  const int minute, const int second,
31908334c51SBrooks Davis                                  const int microsecond)
32008334c51SBrooks Davis {
32108334c51SBrooks Davis     PRE(year >= 1900);
32208334c51SBrooks Davis     PRE(month >= 1 && month <= 12);
32308334c51SBrooks Davis     PRE(day >= 1 && day <= 30);
32408334c51SBrooks Davis     PRE(hour >= 0 && hour <= 23);
32508334c51SBrooks Davis     PRE(minute >= 0 && minute <= 59);
32608334c51SBrooks Davis     PRE(second >= 0 && second <= 60);
32708334c51SBrooks Davis     PRE(microsecond >= 0 && microsecond <= 999999);
32808334c51SBrooks Davis 
32908334c51SBrooks Davis     // The code below is quite convoluted.  The problem is that we can't assume
33008334c51SBrooks Davis     // that some fields (like tm_zone) of ::tm exist, and thus we can't blindly
33108334c51SBrooks Davis     // set them from the code.  Instead of detecting their presence in the
33208334c51SBrooks Davis     // configure script, we just query the current time to initialize such
33308334c51SBrooks Davis     // fields and then we override the ones we are interested in.  (There might
33408334c51SBrooks Davis     // be some better way to do this, but I don't know it and the documentation
33508334c51SBrooks Davis     // does not shed much light into how to create your own fake date.)
33608334c51SBrooks Davis 
33708334c51SBrooks Davis     const time_t current_time = ::time(NULL);
33808334c51SBrooks Davis 
33908334c51SBrooks Davis     ::tm timedata;
34008334c51SBrooks Davis     if (::gmtime_r(&current_time, &timedata) == NULL)
34108334c51SBrooks Davis         UNREACHABLE;
34208334c51SBrooks Davis 
34308334c51SBrooks Davis     timedata.tm_sec = second;
34408334c51SBrooks Davis     timedata.tm_min = minute;
34508334c51SBrooks Davis     timedata.tm_hour = hour;
34608334c51SBrooks Davis     timedata.tm_mday = day;
34708334c51SBrooks Davis     timedata.tm_mon = month - 1;
34808334c51SBrooks Davis     timedata.tm_year = year - 1900;
34908334c51SBrooks Davis     // Ignored: timedata.tm_wday
35008334c51SBrooks Davis     // Ignored: timedata.tm_yday
35108334c51SBrooks Davis 
35208334c51SBrooks Davis     ::timeval data;
35308334c51SBrooks Davis     data.tv_sec = ::mktime(&timedata);
35408334c51SBrooks Davis     data.tv_usec = static_cast< suseconds_t >(microsecond);
35508334c51SBrooks Davis     return timestamp(std::shared_ptr< impl >(new impl(data)));
35608334c51SBrooks Davis }
35708334c51SBrooks Davis 
35808334c51SBrooks Davis 
35908334c51SBrooks Davis /// Constructs a new timestamp representing the current time in UTC.
36008334c51SBrooks Davis ///
36108334c51SBrooks Davis /// \return A new timestamp.
36208334c51SBrooks Davis datetime::timestamp
now(void)36308334c51SBrooks Davis datetime::timestamp::now(void)
36408334c51SBrooks Davis {
36508334c51SBrooks Davis     if (mock_now)
36608334c51SBrooks Davis         return mock_now.get();
36708334c51SBrooks Davis 
36808334c51SBrooks Davis     ::timeval data;
36908334c51SBrooks Davis     {
37008334c51SBrooks Davis         const int ret = ::gettimeofday(&data, NULL);
37108334c51SBrooks Davis         INV(ret != -1);
37208334c51SBrooks Davis     }
37308334c51SBrooks Davis 
37408334c51SBrooks Davis     return timestamp(std::shared_ptr< impl >(new impl(data)));
37508334c51SBrooks Davis }
37608334c51SBrooks Davis 
37708334c51SBrooks Davis 
37808334c51SBrooks Davis /// Formats a timestamp.
37908334c51SBrooks Davis ///
38008334c51SBrooks Davis /// \param format The format string to use as consumed by strftime(3).
38108334c51SBrooks Davis ///
38208334c51SBrooks Davis /// \return The formatted time.
38308334c51SBrooks Davis std::string
strftime(const std::string & format) const38408334c51SBrooks Davis datetime::timestamp::strftime(const std::string& format) const
38508334c51SBrooks Davis {
38608334c51SBrooks Davis     ::tm timedata;
38708334c51SBrooks Davis     // This conversion to time_t is necessary because tv_sec is not guaranteed
38808334c51SBrooks Davis     // to be a time_t.  For example, it isn't in NetBSD 5.x
38908334c51SBrooks Davis     ::time_t epoch_seconds;
39008334c51SBrooks Davis     epoch_seconds = _pimpl->data.tv_sec;
39108334c51SBrooks Davis     if (::gmtime_r(&epoch_seconds, &timedata) == NULL)
39208334c51SBrooks Davis         UNREACHABLE_MSG("gmtime_r(3) did not accept the value returned by "
39308334c51SBrooks Davis                         "gettimeofday(2)");
39408334c51SBrooks Davis 
39508334c51SBrooks Davis     char buf[128];
39608334c51SBrooks Davis     if (::strftime(buf, sizeof(buf), format.c_str(), &timedata) == 0)
39708334c51SBrooks Davis         UNREACHABLE_MSG("Arbitrary-long format strings are unimplemented");
39808334c51SBrooks Davis     return buf;
39908334c51SBrooks Davis }
40008334c51SBrooks Davis 
40108334c51SBrooks Davis 
40208334c51SBrooks Davis /// Formats a timestamp with the ISO 8601 standard and in UTC.
40308334c51SBrooks Davis ///
40408334c51SBrooks Davis /// \return A string with the formatted timestamp.
40508334c51SBrooks Davis std::string
to_iso8601_in_utc(void) const40608334c51SBrooks Davis datetime::timestamp::to_iso8601_in_utc(void) const
40708334c51SBrooks Davis {
40808334c51SBrooks Davis     return F("%s.%06sZ") % strftime("%Y-%m-%dT%H:%M:%S") % _pimpl->data.tv_usec;
40908334c51SBrooks Davis }
41008334c51SBrooks Davis 
41108334c51SBrooks Davis 
41208334c51SBrooks Davis /// Returns the number of microseconds since the epoch in UTC.
41308334c51SBrooks Davis ///
41408334c51SBrooks Davis /// \return A number of microseconds.
41508334c51SBrooks Davis int64_t
to_microseconds(void) const41608334c51SBrooks Davis datetime::timestamp::to_microseconds(void) const
41708334c51SBrooks Davis {
41808334c51SBrooks Davis     return static_cast< int64_t >(_pimpl->data.tv_sec) * 1000000 +
41908334c51SBrooks Davis         _pimpl->data.tv_usec;
42008334c51SBrooks Davis }
42108334c51SBrooks Davis 
42208334c51SBrooks Davis 
42308334c51SBrooks Davis /// Returns the number of seconds since the epoch in UTC.
42408334c51SBrooks Davis ///
42508334c51SBrooks Davis /// \return A number of seconds.
42608334c51SBrooks Davis int64_t
to_seconds(void) const42708334c51SBrooks Davis datetime::timestamp::to_seconds(void) const
42808334c51SBrooks Davis {
42908334c51SBrooks Davis     return static_cast< int64_t >(_pimpl->data.tv_sec);
43008334c51SBrooks Davis }
43108334c51SBrooks Davis 
43208334c51SBrooks Davis 
43308334c51SBrooks Davis /// Sets the current time for testing purposes.
43408334c51SBrooks Davis void
set_mock_now(const int year,const int month,const int day,const int hour,const int minute,const int second,const int microsecond)43508334c51SBrooks Davis datetime::set_mock_now(const int year, const int month,
43608334c51SBrooks Davis                        const int day, const int hour,
43708334c51SBrooks Davis                        const int minute, const int second,
43808334c51SBrooks Davis                        const int microsecond)
43908334c51SBrooks Davis {
44008334c51SBrooks Davis     mock_now = timestamp::from_values(year, month, day, hour, minute, second,
44108334c51SBrooks Davis                                       microsecond);
44208334c51SBrooks Davis }
44308334c51SBrooks Davis 
44408334c51SBrooks Davis 
44508334c51SBrooks Davis /// Sets the current time for testing purposes.
44608334c51SBrooks Davis ///
44708334c51SBrooks Davis /// \param mock_now_ The mock timestamp to set the time to.
44808334c51SBrooks Davis void
set_mock_now(const timestamp & mock_now_)44908334c51SBrooks Davis datetime::set_mock_now(const timestamp& mock_now_)
45008334c51SBrooks Davis {
45108334c51SBrooks Davis     mock_now = mock_now_;
45208334c51SBrooks Davis }
45308334c51SBrooks Davis 
45408334c51SBrooks Davis 
45508334c51SBrooks Davis /// Checks if two timestamps are equal.
45608334c51SBrooks Davis ///
45708334c51SBrooks Davis /// \param other The object to compare to.
45808334c51SBrooks Davis ///
45908334c51SBrooks Davis /// \return True if the two timestamps are equals; false otherwise.
46008334c51SBrooks Davis bool
operator ==(const datetime::timestamp & other) const46108334c51SBrooks Davis datetime::timestamp::operator==(const datetime::timestamp& other) const
46208334c51SBrooks Davis {
46308334c51SBrooks Davis     return _pimpl->data.tv_sec == other._pimpl->data.tv_sec &&
46408334c51SBrooks Davis         _pimpl->data.tv_usec == other._pimpl->data.tv_usec;
46508334c51SBrooks Davis }
46608334c51SBrooks Davis 
46708334c51SBrooks Davis 
46808334c51SBrooks Davis /// Checks if two timestamps are different.
46908334c51SBrooks Davis ///
47008334c51SBrooks Davis /// \param other The object to compare to.
47108334c51SBrooks Davis ///
47208334c51SBrooks Davis /// \return True if the two timestamps are different; false otherwise.
47308334c51SBrooks Davis bool
operator !=(const datetime::timestamp & other) const47408334c51SBrooks Davis datetime::timestamp::operator!=(const datetime::timestamp& other) const
47508334c51SBrooks Davis {
47608334c51SBrooks Davis     return !(*this == other);
47708334c51SBrooks Davis }
47808334c51SBrooks Davis 
47908334c51SBrooks Davis 
48008334c51SBrooks Davis /// Checks if a timestamp is before another.
48108334c51SBrooks Davis ///
48208334c51SBrooks Davis /// \param other The object to compare to.
48308334c51SBrooks Davis ///
48408334c51SBrooks Davis /// \return True if this timestamp comes before other; false otherwise.
48508334c51SBrooks Davis bool
operator <(const datetime::timestamp & other) const48608334c51SBrooks Davis datetime::timestamp::operator<(const datetime::timestamp& other) const
48708334c51SBrooks Davis {
48808334c51SBrooks Davis     return to_microseconds() < other.to_microseconds();
48908334c51SBrooks Davis }
49008334c51SBrooks Davis 
49108334c51SBrooks Davis 
49208334c51SBrooks Davis /// Checks if a timestamp is before or equal to another.
49308334c51SBrooks Davis ///
49408334c51SBrooks Davis /// \param other The object to compare to.
49508334c51SBrooks Davis ///
49608334c51SBrooks Davis /// \return True if this timestamp comes before other or is equal to it;
49708334c51SBrooks Davis /// false otherwise.
49808334c51SBrooks Davis bool
operator <=(const datetime::timestamp & other) const49908334c51SBrooks Davis datetime::timestamp::operator<=(const datetime::timestamp& other) const
50008334c51SBrooks Davis {
50108334c51SBrooks Davis     return to_microseconds() <= other.to_microseconds();
50208334c51SBrooks Davis }
50308334c51SBrooks Davis 
50408334c51SBrooks Davis 
50508334c51SBrooks Davis /// Checks if a timestamp is after another.
50608334c51SBrooks Davis ///
50708334c51SBrooks Davis /// \param other The object to compare to.
50808334c51SBrooks Davis ///
50908334c51SBrooks Davis /// \return True if this timestamp comes after other; false otherwise;
51008334c51SBrooks Davis bool
operator >(const datetime::timestamp & other) const51108334c51SBrooks Davis datetime::timestamp::operator>(const datetime::timestamp& other) const
51208334c51SBrooks Davis {
51308334c51SBrooks Davis     return to_microseconds() > other.to_microseconds();
51408334c51SBrooks Davis }
51508334c51SBrooks Davis 
51608334c51SBrooks Davis 
51708334c51SBrooks Davis /// Checks if a timestamp is after or equal to another.
51808334c51SBrooks Davis ///
51908334c51SBrooks Davis /// \param other The object to compare to.
52008334c51SBrooks Davis ///
52108334c51SBrooks Davis /// \return True if this timestamp comes after other or is equal to it;
52208334c51SBrooks Davis /// false otherwise.
52308334c51SBrooks Davis bool
operator >=(const datetime::timestamp & other) const52408334c51SBrooks Davis datetime::timestamp::operator>=(const datetime::timestamp& other) const
52508334c51SBrooks Davis {
52608334c51SBrooks Davis     return to_microseconds() >= other.to_microseconds();
52708334c51SBrooks Davis }
52808334c51SBrooks Davis 
52908334c51SBrooks Davis 
53008334c51SBrooks Davis /// Calculates the addition of a delta to a timestamp.
53108334c51SBrooks Davis ///
53208334c51SBrooks Davis /// \param other The delta to add.
53308334c51SBrooks Davis ///
53408334c51SBrooks Davis /// \return A new timestamp in the future.
53508334c51SBrooks Davis datetime::timestamp
operator +(const datetime::delta & other) const53608334c51SBrooks Davis datetime::timestamp::operator+(const datetime::delta& other) const
53708334c51SBrooks Davis {
53808334c51SBrooks Davis     return datetime::timestamp::from_microseconds(to_microseconds() +
53908334c51SBrooks Davis                                                   other.to_microseconds());
54008334c51SBrooks Davis }
54108334c51SBrooks Davis 
54208334c51SBrooks Davis 
54308334c51SBrooks Davis /// Calculates the addition of a delta to this timestamp.
54408334c51SBrooks Davis ///
54508334c51SBrooks Davis /// \param other The delta to add.
54608334c51SBrooks Davis ///
54708334c51SBrooks Davis /// \return A reference to the modified timestamp.
54808334c51SBrooks Davis datetime::timestamp&
operator +=(const datetime::delta & other)54908334c51SBrooks Davis datetime::timestamp::operator+=(const datetime::delta& other)
55008334c51SBrooks Davis {
55108334c51SBrooks Davis     *this = *this + other;
55208334c51SBrooks Davis     return *this;
55308334c51SBrooks Davis }
55408334c51SBrooks Davis 
55508334c51SBrooks Davis 
55608334c51SBrooks Davis /// Calculates the subtraction of a delta from a timestamp.
55708334c51SBrooks Davis ///
55808334c51SBrooks Davis /// \param other The delta to subtract.
55908334c51SBrooks Davis ///
56008334c51SBrooks Davis /// \return A new timestamp in the past.
56108334c51SBrooks Davis datetime::timestamp
operator -(const datetime::delta & other) const56208334c51SBrooks Davis datetime::timestamp::operator-(const datetime::delta& other) const
56308334c51SBrooks Davis {
56408334c51SBrooks Davis     return datetime::timestamp::from_microseconds(to_microseconds() -
56508334c51SBrooks Davis                                                   other.to_microseconds());
56608334c51SBrooks Davis }
56708334c51SBrooks Davis 
56808334c51SBrooks Davis 
56908334c51SBrooks Davis /// Calculates the subtraction of a delta from this timestamp.
57008334c51SBrooks Davis ///
57108334c51SBrooks Davis /// \param other The delta to subtract.
57208334c51SBrooks Davis ///
57308334c51SBrooks Davis /// \return A reference to the modified timestamp.
57408334c51SBrooks Davis datetime::timestamp&
operator -=(const datetime::delta & other)57508334c51SBrooks Davis datetime::timestamp::operator-=(const datetime::delta& other)
57608334c51SBrooks Davis {
57708334c51SBrooks Davis     *this = *this - other;
57808334c51SBrooks Davis     return *this;
57908334c51SBrooks Davis }
58008334c51SBrooks Davis 
58108334c51SBrooks Davis 
58208334c51SBrooks Davis /// Calculates the delta between two timestamps.
58308334c51SBrooks Davis ///
58408334c51SBrooks Davis /// \param other The subtrahend.
58508334c51SBrooks Davis ///
58608334c51SBrooks Davis /// \return The difference between this object and the other object.
58708334c51SBrooks Davis ///
58808334c51SBrooks Davis /// \throw std::runtime_error If the subtraction would result in a negative time
58908334c51SBrooks Davis ///     delta, which are currently not supported.
59008334c51SBrooks Davis datetime::delta
operator -(const datetime::timestamp & other) const59108334c51SBrooks Davis datetime::timestamp::operator-(const datetime::timestamp& other) const
59208334c51SBrooks Davis {
593d0738729SEnji Cooper     /*
594d0738729SEnji Cooper      * XXX-BD: gettimeofday isn't necessarily monotonic so return the
595d0738729SEnji Cooper      * smallest non-zero delta if time went backwards.
596d0738729SEnji Cooper      */
597d0738729SEnji Cooper     if ((*this) < other)
598d0738729SEnji Cooper         return datetime::delta::from_microseconds(1);
59908334c51SBrooks Davis     return datetime::delta::from_microseconds(to_microseconds() -
60008334c51SBrooks Davis                                               other.to_microseconds());
60108334c51SBrooks Davis }
60208334c51SBrooks Davis 
60308334c51SBrooks Davis 
60408334c51SBrooks Davis /// Injects the object into a stream.
60508334c51SBrooks Davis ///
60608334c51SBrooks Davis /// \param output The stream into which to inject the object.
60708334c51SBrooks Davis /// \param object The object to format.
60808334c51SBrooks Davis ///
60908334c51SBrooks Davis /// \return The output stream.
61008334c51SBrooks Davis std::ostream&
operator <<(std::ostream & output,const timestamp & object)61108334c51SBrooks Davis datetime::operator<<(std::ostream& output, const timestamp& object)
61208334c51SBrooks Davis {
61308334c51SBrooks Davis     return (output << object.to_microseconds() << "us");
61408334c51SBrooks Davis }
615