1 // Copyright 2010 Google Inc.
2 // All rights reserved.
4 // Redistribution and use in source and binary forms, with or without
5 // modification, are permitted provided that the following conditions are
8 // * Redistributions of source code must retain the above copyright
9 // notice, this list of conditions and the following disclaimer.
10 // * Redistributions in binary form must reproduce the above copyright
11 // notice, this list of conditions and the following disclaimer in the
12 // documentation and/or other materials provided with the distribution.
13 // * Neither the name of Google Inc. nor the names of its contributors
14 // may be used to endorse or promote products derived from this software
15 // without specific prior written permission.
17 // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
18 // "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
19 // LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
20 // A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
21 // OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
22 // SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
23 // LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
24 // DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
25 // THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
26 // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
27 // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
29 #include "utils/datetime.hpp"
37 #include "utils/optional.ipp"
38 #include "utils/sanity.hpp"
40 namespace datetime
= utils::datetime
;
43 using utils::optional
;
49 /// Fake value for the current time.
50 static optional
< datetime::timestamp
> mock_now
= none
;
53 } // anonymous namespace
56 /// Creates a zero time delta.
57 datetime::delta::delta(void) :
64 /// Creates a time delta.
66 /// \param seconds_ The seconds in the delta.
67 /// \param useconds_ The microseconds in the delta.
68 datetime::delta::delta(const int64_t seconds_
,
69 const unsigned long useconds_
) :
76 /// Converts a time expressed in microseconds to a delta.
78 /// \param useconds The amount of microseconds representing the delta.
80 /// \return A new delta object.
82 datetime::delta::from_microseconds(const int64_t useconds
)
84 return delta(useconds
/ 1000000, useconds
% 1000000);
88 /// Convers the delta to a flat representation expressed in microseconds.
90 /// \return The amount of microseconds that corresponds to this delta.
92 datetime::delta::to_microseconds(void) const
94 return seconds
* 1000000 + useconds
;
98 /// Checks if two time deltas are equal.
100 /// \param other The object to compare to.
102 /// \return True if the two time deltas are equals; false otherwise.
104 datetime::delta::operator==(const datetime::delta
& other
) const
106 return seconds
== other
.seconds
&& useconds
== other
.useconds
;
110 /// Checks if two time deltas are different.
112 /// \param other The object to compare to.
114 /// \return True if the two time deltas are different; false otherwise.
116 datetime::delta::operator!=(const datetime::delta
& other
) const
118 return !(*this == other
);
122 /// Adds a time delta to this one.
124 /// \param other The time delta to add.
126 /// \return The addition of this time delta with the other time delta.
128 datetime::delta::operator+(const datetime::delta
& other
) const
130 return delta::from_microseconds(to_microseconds() +
131 other
.to_microseconds());
135 /// Adds a time delta to this one and updates this with the result.
137 /// \param other The time delta to add.
139 /// \return The addition of this time delta with the other time delta.
141 datetime::delta::operator+=(const datetime::delta
& other
)
143 *this = *this + other
;
148 /// Injects the object into a stream.
150 /// \param output The stream into which to inject the object.
151 /// \param object The object to format.
153 /// \return The output stream.
155 datetime::operator<<(std::ostream
& output
, const delta
& object
)
157 return (output
<< object
.to_microseconds() << "us");
165 /// Internal representation for datetime::timestamp.
166 struct timestamp::impl
{
167 /// The raw timestamp as provided by libc.
170 /// Constructs an impl object from initialized data.
172 /// \param data_ The raw timestamp to use.
173 impl(const ::timeval
& data_
) : data(data_
)
179 } // namespace datetime
183 /// Constructs a new timestamp.
185 /// \param pimpl_ An existing impl representation.
186 datetime::timestamp::timestamp(std::shared_ptr
< impl
> pimpl_
) :
192 /// Constructs a timestamp from the amount of microseconds since the epoch.
194 /// \param value Microseconds since the epoch in UTC. Must be positive.
196 /// \return A new timestamp.
198 datetime::timestamp::from_microseconds(const int64_t value
)
202 data
.tv_sec
= static_cast< time_t >(value
/ 1000000);
203 data
.tv_usec
= static_cast< suseconds_t
>(value
% 1000000);
204 return timestamp(std::shared_ptr
< impl
>(new impl(data
)));
208 /// Constructs a timestamp based on user-friendly values.
210 /// \param year The year in the [1900,inf) range.
211 /// \param month The month in the [1,12] range.
212 /// \param day The day in the [1,30] range.
213 /// \param hour The hour in the [0,23] range.
214 /// \param minute The minute in the [0,59] range.
215 /// \param second The second in the [0,60] range. Yes, that is 60, which can be
216 /// the case on leap seconds.
217 /// \param microsecond The microsecond in the [0,999999] range.
219 /// \return A new timestamp.
221 datetime::timestamp::from_values(const int year
, const int month
,
222 const int day
, const int hour
,
223 const int minute
, const int second
,
224 const int microsecond
)
227 PRE(month
>= 1 && month
<= 12);
228 PRE(day
>= 1 && day
<= 30);
229 PRE(hour
>= 0 && hour
<= 23);
230 PRE(minute
>= 0 && minute
<= 59);
231 PRE(second
>= 0 && second
<= 60);
232 PRE(microsecond
>= 0 && microsecond
<= 999999);
234 // The code below is quite convoluted. The problem is that we can't assume
235 // that some fields (like tm_zone) of ::tm exist, and thus we can't blindly
236 // set them from the code. Instead of detecting their presence in the
237 // configure script, we just query the current time to initialize such
238 // fields and then we override the ones we are interested in. (There might
239 // be some better way to do this, but I don't know it and the documentation
240 // does not shed much light into how to create your own fake date.)
242 const time_t current_time
= ::time(NULL
);
245 if (::gmtime_r(¤t_time
, &timedata
) == NULL
)
248 timedata
.tm_sec
= second
;
249 timedata
.tm_min
= minute
;
250 timedata
.tm_hour
= hour
;
251 timedata
.tm_mday
= day
;
252 timedata
.tm_mon
= month
- 1;
253 timedata
.tm_year
= year
- 1900;
254 // Ignored: timedata.tm_wday
255 // Ignored: timedata.tm_yday
258 data
.tv_sec
= ::mktime(&timedata
);
259 data
.tv_usec
= static_cast< suseconds_t
>(microsecond
);
260 return timestamp(std::shared_ptr
< impl
>(new impl(data
)));
264 /// Constructs a new timestamp representing the current time in UTC.
266 /// \return A new timestamp.
268 datetime::timestamp::now(void)
271 return mock_now
.get();
275 #if defined(__minix) && !defined(NDEBUG)
277 #endif /* defined(__minix) && !defined(NDEBUG) */
278 ::gettimeofday(&data
, NULL
);
282 return timestamp(std::shared_ptr
< impl
>(new impl(data
)));
286 /// Formats a timestamp.
288 /// \param format The format string to use as consumed by strftime(3).
290 /// \return The formatted time.
292 datetime::timestamp::strftime(const std::string
& format
) const
295 // This conversion to time_t is necessary because tv_sec is not guaranteed
296 // to be a time_t. For example, it isn't in NetBSD 5.x
297 ::time_t epoch_seconds
;
298 epoch_seconds
= _pimpl
->data
.tv_sec
;
299 if (::gmtime_r(&epoch_seconds
, &timedata
) == NULL
)
300 UNREACHABLE_MSG("gmtime_r(3) did not accept the value returned by "
304 if (::strftime(buf
, sizeof(buf
), format
.c_str(), &timedata
) == 0)
305 UNREACHABLE_MSG("Arbitrary-long format strings are unimplemented");
310 /// Returns the number of microseconds since the epoch in UTC.
312 /// \return A number of microseconds.
314 datetime::timestamp::to_microseconds(void) const
316 return static_cast< int64_t >(_pimpl
->data
.tv_sec
) * 1000000 +
317 _pimpl
->data
.tv_usec
;
321 /// Returns the number of seconds since the epoch in UTC.
323 /// \return A number of seconds.
325 datetime::timestamp::to_seconds(void) const
327 return static_cast< int64_t >(_pimpl
->data
.tv_sec
);
331 /// Sets the current time for testing purposes.
333 datetime::set_mock_now(const int year
, const int month
,
334 const int day
, const int hour
,
335 const int minute
, const int second
,
336 const int microsecond
)
338 mock_now
= timestamp::from_values(year
, month
, day
, hour
, minute
, second
,
343 /// Checks if two timestamps are equal.
345 /// \param other The object to compare to.
347 /// \return True if the two timestamps are equals; false otherwise.
349 datetime::timestamp::operator==(const datetime::timestamp
& other
) const
351 return _pimpl
->data
.tv_sec
== other
._pimpl
->data
.tv_sec
&&
352 _pimpl
->data
.tv_usec
== other
._pimpl
->data
.tv_usec
;
356 /// Checks if two timestamps are different.
358 /// \param other The object to compare to.
360 /// \return True if the two timestamps are different; false otherwise.
362 datetime::timestamp::operator!=(const datetime::timestamp
& other
) const
364 return !(*this == other
);
368 /// Calculates the delta between two timestamps.
370 /// \param other The subtrahend.
372 /// \return The difference between this object and the other object.
374 datetime::timestamp::operator-(const datetime::timestamp
& other
) const
376 return datetime::delta::from_microseconds(to_microseconds() -
377 other
.to_microseconds());
381 /// Injects the object into a stream.
383 /// \param output The stream into which to inject the object.
384 /// \param object The object to format.
386 /// \return The output stream.
388 datetime::operator<<(std::ostream
& output
, const timestamp
& object
)
390 return (output
<< object
.to_microseconds() << "us");