1 // Copyright 2010 Google Inc.
2 // All rights reserved.
3 //
4 // Redistribution and use in source and binary forms, with or without
5 // modification, are permitted provided that the following conditions are
6 // met:
7 //
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.
16 //
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.
28
29 #include "utils/datetime.hpp"
30
31 extern "C" {
32 #include <sys/time.h>
33
34 #include <time.h>
35 }
36
37 #include "utils/optional.ipp"
38 #include "utils/sanity.hpp"
39
40 namespace datetime = utils::datetime;
41
42 using utils::none;
43 using utils::optional;
44
45
46 namespace {
47
48
49 /// Fake value for the current time.
50 static optional< datetime::timestamp > mock_now = none;
51
52
53 } // anonymous namespace
54
55
56 /// Creates a zero time delta.
delta(void)57 datetime::delta::delta(void) :
58 seconds(0),
59 useconds(0)
60 {
61 }
62
63
64 /// Creates a time delta.
65 ///
66 /// \param seconds_ The seconds in the delta.
67 /// \param useconds_ The microseconds in the delta.
delta(const int64_t seconds_,const unsigned long useconds_)68 datetime::delta::delta(const int64_t seconds_,
69 const unsigned long useconds_) :
70 seconds(seconds_),
71 useconds(useconds_)
72 {
73 }
74
75
76 /// Converts a time expressed in microseconds to a delta.
77 ///
78 /// \param useconds The amount of microseconds representing the delta.
79 ///
80 /// \return A new delta object.
81 datetime::delta
from_microseconds(const int64_t useconds)82 datetime::delta::from_microseconds(const int64_t useconds)
83 {
84 return delta(useconds / 1000000, useconds % 1000000);
85 }
86
87
88 /// Convers the delta to a flat representation expressed in microseconds.
89 ///
90 /// \return The amount of microseconds that corresponds to this delta.
91 int64_t
to_microseconds(void) const92 datetime::delta::to_microseconds(void) const
93 {
94 return seconds * 1000000 + useconds;
95 }
96
97
98 /// Checks if two time deltas are equal.
99 ///
100 /// \param other The object to compare to.
101 ///
102 /// \return True if the two time deltas are equals; false otherwise.
103 bool
operator ==(const datetime::delta & other) const104 datetime::delta::operator==(const datetime::delta& other) const
105 {
106 return seconds == other.seconds && useconds == other.useconds;
107 }
108
109
110 /// Checks if two time deltas are different.
111 ///
112 /// \param other The object to compare to.
113 ///
114 /// \return True if the two time deltas are different; false otherwise.
115 bool
operator !=(const datetime::delta & other) const116 datetime::delta::operator!=(const datetime::delta& other) const
117 {
118 return !(*this == other);
119 }
120
121
122 /// Adds a time delta to this one.
123 ///
124 /// \param other The time delta to add.
125 ///
126 /// \return The addition of this time delta with the other time delta.
127 datetime::delta
operator +(const datetime::delta & other) const128 datetime::delta::operator+(const datetime::delta& other) const
129 {
130 return delta::from_microseconds(to_microseconds() +
131 other.to_microseconds());
132 }
133
134
135 /// Adds a time delta to this one and updates this with the result.
136 ///
137 /// \param other The time delta to add.
138 ///
139 /// \return The addition of this time delta with the other time delta.
140 datetime::delta
operator +=(const datetime::delta & other)141 datetime::delta::operator+=(const datetime::delta& other)
142 {
143 *this = *this + other;
144 return *this;
145 }
146
147
148 /// Injects the object into a stream.
149 ///
150 /// \param output The stream into which to inject the object.
151 /// \param object The object to format.
152 ///
153 /// \return The output stream.
154 std::ostream&
operator <<(std::ostream & output,const delta & object)155 datetime::operator<<(std::ostream& output, const delta& object)
156 {
157 return (output << object.to_microseconds() << "us");
158 }
159
160
161 namespace utils {
162 namespace datetime {
163
164
165 /// Internal representation for datetime::timestamp.
166 struct timestamp::impl {
167 /// The raw timestamp as provided by libc.
168 ::timeval data;
169
170 /// Constructs an impl object from initialized data.
171 ///
172 /// \param data_ The raw timestamp to use.
implutils::datetime::timestamp::impl173 impl(const ::timeval& data_) : data(data_)
174 {
175 }
176 };
177
178
179 } // namespace datetime
180 } // namespace utils
181
182
183 /// Constructs a new timestamp.
184 ///
185 /// \param pimpl_ An existing impl representation.
timestamp(std::shared_ptr<impl> pimpl_)186 datetime::timestamp::timestamp(std::shared_ptr< impl > pimpl_) :
187 _pimpl(pimpl_)
188 {
189 }
190
191
192 /// Constructs a timestamp from the amount of microseconds since the epoch.
193 ///
194 /// \param value Microseconds since the epoch in UTC. Must be positive.
195 ///
196 /// \return A new timestamp.
197 datetime::timestamp
from_microseconds(const int64_t value)198 datetime::timestamp::from_microseconds(const int64_t value)
199 {
200 PRE(value >= 0);
201 ::timeval data;
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)));
205 }
206
207
208 /// Constructs a timestamp based on user-friendly values.
209 ///
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.
218 ///
219 /// \return A new timestamp.
220 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)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)
225 {
226 PRE(year >= 1900);
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);
233
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.)
241
242 const time_t current_time = ::time(NULL);
243
244 ::tm timedata;
245 if (::gmtime_r(¤t_time, &timedata) == NULL)
246 UNREACHABLE;
247
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
256
257 ::timeval data;
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)));
261 }
262
263
264 /// Constructs a new timestamp representing the current time in UTC.
265 ///
266 /// \return A new timestamp.
267 datetime::timestamp
now(void)268 datetime::timestamp::now(void)
269 {
270 if (mock_now)
271 return mock_now.get();
272
273 ::timeval data;
274 {
275 const int ret = ::gettimeofday(&data, NULL);
276 INV(ret != -1);
277 }
278
279 return timestamp(std::shared_ptr< impl >(new impl(data)));
280 }
281
282
283 /// Formats a timestamp.
284 ///
285 /// \param format The format string to use as consumed by strftime(3).
286 ///
287 /// \return The formatted time.
288 std::string
strftime(const std::string & format) const289 datetime::timestamp::strftime(const std::string& format) const
290 {
291 ::tm timedata;
292 // This conversion to time_t is necessary because tv_sec is not guaranteed
293 // to be a time_t. For example, it isn't in NetBSD 5.x
294 ::time_t epoch_seconds;
295 epoch_seconds = _pimpl->data.tv_sec;
296 if (::gmtime_r(&epoch_seconds, &timedata) == NULL)
297 UNREACHABLE_MSG("gmtime_r(3) did not accept the value returned by "
298 "gettimeofday(2)");
299
300 char buf[128];
301 if (::strftime(buf, sizeof(buf), format.c_str(), &timedata) == 0)
302 UNREACHABLE_MSG("Arbitrary-long format strings are unimplemented");
303 return buf;
304 }
305
306
307 /// Returns the number of microseconds since the epoch in UTC.
308 ///
309 /// \return A number of microseconds.
310 int64_t
to_microseconds(void) const311 datetime::timestamp::to_microseconds(void) const
312 {
313 return static_cast< int64_t >(_pimpl->data.tv_sec) * 1000000 +
314 _pimpl->data.tv_usec;
315 }
316
317
318 /// Returns the number of seconds since the epoch in UTC.
319 ///
320 /// \return A number of seconds.
321 int64_t
to_seconds(void) const322 datetime::timestamp::to_seconds(void) const
323 {
324 return static_cast< int64_t >(_pimpl->data.tv_sec);
325 }
326
327
328 /// Sets the current time for testing purposes.
329 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)330 datetime::set_mock_now(const int year, const int month,
331 const int day, const int hour,
332 const int minute, const int second,
333 const int microsecond)
334 {
335 mock_now = timestamp::from_values(year, month, day, hour, minute, second,
336 microsecond);
337 }
338
339
340 /// Checks if two timestamps are equal.
341 ///
342 /// \param other The object to compare to.
343 ///
344 /// \return True if the two timestamps are equals; false otherwise.
345 bool
operator ==(const datetime::timestamp & other) const346 datetime::timestamp::operator==(const datetime::timestamp& other) const
347 {
348 return _pimpl->data.tv_sec == other._pimpl->data.tv_sec &&
349 _pimpl->data.tv_usec == other._pimpl->data.tv_usec;
350 }
351
352
353 /// Checks if two timestamps are different.
354 ///
355 /// \param other The object to compare to.
356 ///
357 /// \return True if the two timestamps are different; false otherwise.
358 bool
operator !=(const datetime::timestamp & other) const359 datetime::timestamp::operator!=(const datetime::timestamp& other) const
360 {
361 return !(*this == other);
362 }
363
364
365 /// Calculates the delta between two timestamps.
366 ///
367 /// \param other The subtrahend.
368 ///
369 /// \return The difference between this object and the other object.
370 datetime::delta
operator -(const datetime::timestamp & other) const371 datetime::timestamp::operator-(const datetime::timestamp& other) const
372 {
373 return datetime::delta::from_microseconds(to_microseconds() -
374 other.to_microseconds());
375 }
376
377
378 /// Injects the object into a stream.
379 ///
380 /// \param output The stream into which to inject the object.
381 /// \param object The object to format.
382 ///
383 /// \return The output stream.
384 std::ostream&
operator <<(std::ostream & output,const timestamp & object)385 datetime::operator<<(std::ostream& output, const timestamp& object)
386 {
387 return (output << object.to_microseconds() << "us");
388 }
389