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 31 extern "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 44 namespace datetime = utils::datetime; 45 46 using utils::none; 47 using utils::optional; 48 49 50 namespace { 51 52 53 /// Fake value for the current time. 54 static optional< datetime::timestamp > mock_now = none; 55 56 57 } // anonymous namespace 58 59 60 /// Creates a zero time delta. 61 datetime::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. 74 datetime::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. 93 datetime::delta 94 datetime::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. 109 int64_t 110 datetime::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. 121 bool 122 datetime::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. 133 bool 134 datetime::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. 145 bool 146 datetime::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. 159 bool 160 datetime::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. 171 bool 172 datetime::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. 185 bool 186 datetime::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. 197 datetime::delta 198 datetime::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. 210 datetime::delta& 211 datetime::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. 223 datetime::delta 224 datetime::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. 235 datetime::delta& 236 datetime::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. 249 std::ostream& 250 datetime::operator<<(std::ostream& output, const delta& object) 251 { 252 return (output << object.to_microseconds() << "us"); 253 } 254 255 256 namespace utils { 257 namespace datetime { 258 259 260 /// Internal representation for datetime::timestamp. 261 struct 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. 281 datetime::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. 292 datetime::timestamp 293 datetime::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. 315 datetime::timestamp 316 datetime::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. 362 datetime::timestamp 363 datetime::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. 383 std::string 384 datetime::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. 405 std::string 406 datetime::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. 415 int64_t 416 datetime::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. 426 int64_t 427 datetime::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. 434 void 435 datetime::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. 448 void 449 datetime::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. 460 bool 461 datetime::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. 473 bool 474 datetime::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. 485 bool 486 datetime::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. 498 bool 499 datetime::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; 510 bool 511 datetime::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. 523 bool 524 datetime::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. 535 datetime::timestamp 536 datetime::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. 548 datetime::timestamp& 549 datetime::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. 561 datetime::timestamp 562 datetime::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. 574 datetime::timestamp& 575 datetime::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. 590 datetime::delta 591 datetime::timestamp::operator-(const datetime::timestamp& other) const 592 { 593 if ((*this) < other) { 594 throw std::runtime_error( 595 F("Cannot subtract %s from %s as it would result in a negative " 596 "datetime::delta, which are not supported") % other % (*this)); 597 } 598 return datetime::delta::from_microseconds(to_microseconds() - 599 other.to_microseconds()); 600 } 601 602 603 /// Injects the object into a stream. 604 /// 605 /// \param output The stream into which to inject the object. 606 /// \param object The object to format. 607 /// 608 /// \return The output stream. 609 std::ostream& 610 datetime::operator<<(std::ostream& output, const timestamp& object) 611 { 612 return (output << object.to_microseconds() << "us"); 613 } 614