1// Copyright 2010 The Kyua Authors. 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 31extern "C" { 32#include <sys/time.h> 33 34#include <time.h> 35} 36 37#include <stdexcept> 38 39#include "utils/format/macros.hpp" 40#include "utils/optional.ipp" 41#include "utils/noncopyable.hpp" 42#include "utils/sanity.hpp" 43 44namespace datetime = utils::datetime; 45 46using utils::none; 47using utils::optional; 48 49 50namespace { 51 52 53/// Fake value for the current time. 54static optional< datetime::timestamp > mock_now = none; 55 56 57} // anonymous namespace 58 59 60/// Creates a zero time delta. 61datetime::delta::delta(void) : 62 seconds(0), 63 useconds(0) 64{ 65} 66 67 68/// Creates a time delta. 69/// 70/// \param seconds_ The seconds in the delta. 71/// \param useconds_ The microseconds in the delta. 72/// 73/// \throw std::runtime_error If the input delta is negative. 74datetime::delta::delta(const int64_t seconds_, 75 const unsigned long useconds_) : 76 seconds(seconds_), 77 useconds(useconds_) 78{ 79 if (seconds_ < 0) { 80 throw std::runtime_error(F("Negative deltas are not supported by the " 81 "datetime::delta class; got: %s") % (*this)); 82 } 83} 84 85 86/// Converts a time expressed in microseconds to a delta. 87/// 88/// \param useconds The amount of microseconds representing the delta. 89/// 90/// \return A new delta object. 91/// 92/// \throw std::runtime_error If the input delta is negative. 93datetime::delta 94datetime::delta::from_microseconds(const int64_t useconds) 95{ 96 if (useconds < 0) { 97 throw std::runtime_error(F("Negative deltas are not supported by the " 98 "datetime::delta class; got: %sus") % 99 useconds); 100 } 101 102 return delta(useconds / 1000000, useconds % 1000000); 103} 104 105 106/// Convers the delta to a flat representation expressed in microseconds. 107/// 108/// \return The amount of microseconds that corresponds to this delta. 109int64_t 110datetime::delta::to_microseconds(void) const 111{ 112 return seconds * 1000000 + useconds; 113} 114 115 116/// Checks if two time deltas are equal. 117/// 118/// \param other The object to compare to. 119/// 120/// \return True if the two time deltas are equals; false otherwise. 121bool 122datetime::delta::operator==(const datetime::delta& other) const 123{ 124 return seconds == other.seconds && useconds == other.useconds; 125} 126 127 128/// Checks if two time deltas are different. 129/// 130/// \param other The object to compare to. 131/// 132/// \return True if the two time deltas are different; false otherwise. 133bool 134datetime::delta::operator!=(const datetime::delta& other) const 135{ 136 return !(*this == other); 137} 138 139 140/// Checks if this time delta is shorter than another one. 141/// 142/// \param other The object to compare to. 143/// 144/// \return True if this time delta is shorter than other; false otherwise. 145bool 146datetime::delta::operator<(const datetime::delta& other) const 147{ 148 return seconds < other.seconds || 149 (seconds == other.seconds && useconds < other.useconds); 150} 151 152 153/// Checks if this time delta is shorter than or equal to another one. 154/// 155/// \param other The object to compare to. 156/// 157/// \return True if this time delta is shorter than or equal to; false 158/// otherwise. 159bool 160datetime::delta::operator<=(const datetime::delta& other) const 161{ 162 return (*this) < other || (*this) == other; 163} 164 165 166/// Checks if this time delta is larger than another one. 167/// 168/// \param other The object to compare to. 169/// 170/// \return True if this time delta is larger than other; false otherwise. 171bool 172datetime::delta::operator>(const datetime::delta& other) const 173{ 174 return seconds > other.seconds || 175 (seconds == other.seconds && useconds > other.useconds); 176} 177 178 179/// Checks if this time delta is larger than or equal to another one. 180/// 181/// \param other The object to compare to. 182/// 183/// \return True if this time delta is larger than or equal to; false 184/// otherwise. 185bool 186datetime::delta::operator>=(const datetime::delta& other) const 187{ 188 return (*this) > other || (*this) == other; 189} 190 191 192/// Adds a time delta to this one. 193/// 194/// \param other The time delta to add. 195/// 196/// \return The addition of this time delta with the other time delta. 197datetime::delta 198datetime::delta::operator+(const datetime::delta& other) const 199{ 200 return delta::from_microseconds(to_microseconds() + 201 other.to_microseconds()); 202} 203 204 205/// Adds a time delta to this one and updates this with the result. 206/// 207/// \param other The time delta to add. 208/// 209/// \return The addition of this time delta with the other time delta. 210datetime::delta& 211datetime::delta::operator+=(const datetime::delta& other) 212{ 213 *this = *this + other; 214 return *this; 215} 216 217 218/// Scales this delta by a positive integral factor. 219/// 220/// \param factor The scaling factor. 221/// 222/// \return The scaled delta. 223datetime::delta 224datetime::delta::operator*(const std::size_t factor) const 225{ 226 return delta::from_microseconds(to_microseconds() * factor); 227} 228 229 230/// Scales this delta by and updates this delta with the result. 231/// 232/// \param factor The scaling factor. 233/// 234/// \return The scaled delta as a reference to the input object. 235datetime::delta& 236datetime::delta::operator*=(const std::size_t factor) 237{ 238 *this = *this * factor; 239 return *this; 240} 241 242 243/// Injects the object into a stream. 244/// 245/// \param output The stream into which to inject the object. 246/// \param object The object to format. 247/// 248/// \return The output stream. 249std::ostream& 250datetime::operator<<(std::ostream& output, const delta& object) 251{ 252 return (output << object.to_microseconds() << "us"); 253} 254 255 256namespace utils { 257namespace datetime { 258 259 260/// Internal representation for datetime::timestamp. 261struct timestamp::impl : utils::noncopyable { 262 /// The raw timestamp as provided by libc. 263 ::timeval data; 264 265 /// Constructs an impl object from initialized data. 266 /// 267 /// \param data_ The raw timestamp to use. 268 impl(const ::timeval& data_) : data(data_) 269 { 270 } 271}; 272 273 274} // namespace datetime 275} // namespace utils 276 277 278/// Constructs a new timestamp. 279/// 280/// \param pimpl_ An existing impl representation. 281datetime::timestamp::timestamp(std::shared_ptr< impl > pimpl_) : 282 _pimpl(pimpl_) 283{ 284} 285 286 287/// Constructs a timestamp from the amount of microseconds since the epoch. 288/// 289/// \param value Microseconds since the epoch in UTC. Must be positive. 290/// 291/// \return A new timestamp. 292datetime::timestamp 293datetime::timestamp::from_microseconds(const int64_t value) 294{ 295 PRE(value >= 0); 296 ::timeval data; 297 data.tv_sec = static_cast< time_t >(value / 1000000); 298 data.tv_usec = static_cast< suseconds_t >(value % 1000000); 299 return timestamp(std::shared_ptr< impl >(new impl(data))); 300} 301 302 303/// Constructs a timestamp based on user-friendly values. 304/// 305/// \param year The year in the [1900,inf) range. 306/// \param month The month in the [1,12] range. 307/// \param day The day in the [1,30] range. 308/// \param hour The hour in the [0,23] range. 309/// \param minute The minute in the [0,59] range. 310/// \param second The second in the [0,60] range. Yes, that is 60, which can be 311/// the case on leap seconds. 312/// \param microsecond The microsecond in the [0,999999] range. 313/// 314/// \return A new timestamp. 315datetime::timestamp 316datetime::timestamp::from_values(const int year, const int month, 317 const int day, const int hour, 318 const int minute, const int second, 319 const int microsecond) 320{ 321 PRE(year >= 1900); 322 PRE(month >= 1 && month <= 12); 323 PRE(day >= 1 && day <= 30); 324 PRE(hour >= 0 && hour <= 23); 325 PRE(minute >= 0 && minute <= 59); 326 PRE(second >= 0 && second <= 60); 327 PRE(microsecond >= 0 && microsecond <= 999999); 328 329 // The code below is quite convoluted. The problem is that we can't assume 330 // that some fields (like tm_zone) of ::tm exist, and thus we can't blindly 331 // set them from the code. Instead of detecting their presence in the 332 // configure script, we just query the current time to initialize such 333 // fields and then we override the ones we are interested in. (There might 334 // be some better way to do this, but I don't know it and the documentation 335 // does not shed much light into how to create your own fake date.) 336 337 const time_t current_time = ::time(NULL); 338 339 ::tm timedata; 340 if (::gmtime_r(¤t_time, &timedata) == NULL) 341 UNREACHABLE; 342 343 timedata.tm_sec = second; 344 timedata.tm_min = minute; 345 timedata.tm_hour = hour; 346 timedata.tm_mday = day; 347 timedata.tm_mon = month - 1; 348 timedata.tm_year = year - 1900; 349 // Ignored: timedata.tm_wday 350 // Ignored: timedata.tm_yday 351 352 ::timeval data; 353 data.tv_sec = ::mktime(&timedata); 354 data.tv_usec = static_cast< suseconds_t >(microsecond); 355 return timestamp(std::shared_ptr< impl >(new impl(data))); 356} 357 358 359/// Constructs a new timestamp representing the current time in UTC. 360/// 361/// \return A new timestamp. 362datetime::timestamp 363datetime::timestamp::now(void) 364{ 365 if (mock_now) 366 return mock_now.get(); 367 368 ::timeval data; 369 { 370 const int ret = ::gettimeofday(&data, NULL); 371 INV(ret != -1); 372 } 373 374 return timestamp(std::shared_ptr< impl >(new impl(data))); 375} 376 377 378/// Formats a timestamp. 379/// 380/// \param format The format string to use as consumed by strftime(3). 381/// 382/// \return The formatted time. 383std::string 384datetime::timestamp::strftime(const std::string& format) const 385{ 386 ::tm timedata; 387 // This conversion to time_t is necessary because tv_sec is not guaranteed 388 // to be a time_t. For example, it isn't in NetBSD 5.x 389 ::time_t epoch_seconds; 390 epoch_seconds = _pimpl->data.tv_sec; 391 if (::gmtime_r(&epoch_seconds, &timedata) == NULL) 392 UNREACHABLE_MSG("gmtime_r(3) did not accept the value returned by " 393 "gettimeofday(2)"); 394 395 char buf[128]; 396 if (::strftime(buf, sizeof(buf), format.c_str(), &timedata) == 0) 397 UNREACHABLE_MSG("Arbitrary-long format strings are unimplemented"); 398 return buf; 399} 400 401 402/// Formats a timestamp with the ISO 8601 standard and in UTC. 403/// 404/// \return A string with the formatted timestamp. 405std::string 406datetime::timestamp::to_iso8601_in_utc(void) const 407{ 408 return F("%s.%06sZ") % strftime("%Y-%m-%dT%H:%M:%S") % _pimpl->data.tv_usec; 409} 410 411 412/// Returns the number of microseconds since the epoch in UTC. 413/// 414/// \return A number of microseconds. 415int64_t 416datetime::timestamp::to_microseconds(void) const 417{ 418 return static_cast< int64_t >(_pimpl->data.tv_sec) * 1000000 + 419 _pimpl->data.tv_usec; 420} 421 422 423/// Returns the number of seconds since the epoch in UTC. 424/// 425/// \return A number of seconds. 426int64_t 427datetime::timestamp::to_seconds(void) const 428{ 429 return static_cast< int64_t >(_pimpl->data.tv_sec); 430} 431 432 433/// Sets the current time for testing purposes. 434void 435datetime::set_mock_now(const int year, const int month, 436 const int day, const int hour, 437 const int minute, const int second, 438 const int microsecond) 439{ 440 mock_now = timestamp::from_values(year, month, day, hour, minute, second, 441 microsecond); 442} 443 444 445/// Sets the current time for testing purposes. 446/// 447/// \param mock_now_ The mock timestamp to set the time to. 448void 449datetime::set_mock_now(const timestamp& mock_now_) 450{ 451 mock_now = mock_now_; 452} 453 454 455/// Checks if two timestamps are equal. 456/// 457/// \param other The object to compare to. 458/// 459/// \return True if the two timestamps are equals; false otherwise. 460bool 461datetime::timestamp::operator==(const datetime::timestamp& other) const 462{ 463 return _pimpl->data.tv_sec == other._pimpl->data.tv_sec && 464 _pimpl->data.tv_usec == other._pimpl->data.tv_usec; 465} 466 467 468/// Checks if two timestamps are different. 469/// 470/// \param other The object to compare to. 471/// 472/// \return True if the two timestamps are different; false otherwise. 473bool 474datetime::timestamp::operator!=(const datetime::timestamp& other) const 475{ 476 return !(*this == other); 477} 478 479 480/// Checks if a timestamp is before another. 481/// 482/// \param other The object to compare to. 483/// 484/// \return True if this timestamp comes before other; false otherwise. 485bool 486datetime::timestamp::operator<(const datetime::timestamp& other) const 487{ 488 return to_microseconds() < other.to_microseconds(); 489} 490 491 492/// Checks if a timestamp is before or equal to another. 493/// 494/// \param other The object to compare to. 495/// 496/// \return True if this timestamp comes before other or is equal to it; 497/// false otherwise. 498bool 499datetime::timestamp::operator<=(const datetime::timestamp& other) const 500{ 501 return to_microseconds() <= other.to_microseconds(); 502} 503 504 505/// Checks if a timestamp is after another. 506/// 507/// \param other The object to compare to. 508/// 509/// \return True if this timestamp comes after other; false otherwise; 510bool 511datetime::timestamp::operator>(const datetime::timestamp& other) const 512{ 513 return to_microseconds() > other.to_microseconds(); 514} 515 516 517/// Checks if a timestamp is after or equal to another. 518/// 519/// \param other The object to compare to. 520/// 521/// \return True if this timestamp comes after other or is equal to it; 522/// false otherwise. 523bool 524datetime::timestamp::operator>=(const datetime::timestamp& other) const 525{ 526 return to_microseconds() >= other.to_microseconds(); 527} 528 529 530/// Calculates the addition of a delta to a timestamp. 531/// 532/// \param other The delta to add. 533/// 534/// \return A new timestamp in the future. 535datetime::timestamp 536datetime::timestamp::operator+(const datetime::delta& other) const 537{ 538 return datetime::timestamp::from_microseconds(to_microseconds() + 539 other.to_microseconds()); 540} 541 542 543/// Calculates the addition of a delta to this timestamp. 544/// 545/// \param other The delta to add. 546/// 547/// \return A reference to the modified timestamp. 548datetime::timestamp& 549datetime::timestamp::operator+=(const datetime::delta& other) 550{ 551 *this = *this + other; 552 return *this; 553} 554 555 556/// Calculates the subtraction of a delta from a timestamp. 557/// 558/// \param other The delta to subtract. 559/// 560/// \return A new timestamp in the past. 561datetime::timestamp 562datetime::timestamp::operator-(const datetime::delta& other) const 563{ 564 return datetime::timestamp::from_microseconds(to_microseconds() - 565 other.to_microseconds()); 566} 567 568 569/// Calculates the subtraction of a delta from this timestamp. 570/// 571/// \param other The delta to subtract. 572/// 573/// \return A reference to the modified timestamp. 574datetime::timestamp& 575datetime::timestamp::operator-=(const datetime::delta& other) 576{ 577 *this = *this - other; 578 return *this; 579} 580 581 582/// Calculates the delta between two timestamps. 583/// 584/// \param other The subtrahend. 585/// 586/// \return The difference between this object and the other object. 587/// 588/// \throw std::runtime_error If the subtraction would result in a negative time 589/// delta, which are currently not supported. 590datetime::delta 591datetime::timestamp::operator-(const datetime::timestamp& other) const 592{ 593 /* 594 * XXX-BD: gettimeofday isn't necessarily monotonic so return the 595 * smallest non-zero delta if time went backwards. 596 */ 597 if ((*this) < other) 598 return datetime::delta::from_microseconds(1); 599 return datetime::delta::from_microseconds(to_microseconds() - 600 other.to_microseconds()); 601} 602 603 604/// Injects the object into a stream. 605/// 606/// \param output The stream into which to inject the object. 607/// \param object The object to format. 608/// 609/// \return The output stream. 610std::ostream& 611datetime::operator<<(std::ostream& output, const timestamp& object) 612{ 613 return (output << object.to_microseconds() << "us"); 614} 615