11

I have a string in the following time format:

"%Y-%m-%d %H:%M:%S.%f"

where the %f is millisec, eg : 14:31:23.946571

I want this as a chrono time_point. Is there a cast to do this?

Nanc
  • 464
  • 5
  • 15
bge0
  • 901
  • 2
  • 10
  • 25
  • Personally I would use the date algorithms here: http://home.roadrunner.com/~hinnant/date_algorithms.html Specifically `days_from_civil`, and then add on H:M:S. You'll have to decide if that is in the UTC timezone or not, and add a timezone offset if it isn't. – Howard Hinnant Aug 19 '14 at 22:08
  • 1
    H:M:S is not the issue. Millisec is really the issue. Otherwise I would have just converted it to a time_t – bge0 Aug 19 '14 at 22:12
  • @HowardHinnant how would you convert today an ISO 8061 date string (incl. miliseconds) to an manipulative data type using your date.h? – Bruno Bieri Dec 26 '17 at 13:30
  • @BrunoBieri: Using [my library](https://howardhinnant.github.io/date/date.html) I would `in >> date::parse("%F %T", tp);` where `tp` is a `std::chrono::system_clock::time_point`, or a `time_point`-based time point with precision milliseconds or finer. One could also use the more verbose string: `"%Y-%m-%d %H:%M:%S"` which is equivalent. Often ISO 8061 formats require a `T` separator between the date and time instead of a space, and if you want that, just put the `T` in the format string. – Howard Hinnant Dec 26 '17 at 15:36

1 Answers1

10

There is no cast from std::string to std::chrono::time_point. You have to build up the std::chrono::time_point object.

  • Use everything but the microseconds to construct a std::tm object (<ctime>). The year should be based at 1900, not 0. The month should be based at 0, not 1.
  • Use std::mktime() to create an std::time_t object.
  • Create a std::chrono::time_point using from_time_t().
  • Add the remaining decimal portion (treated as an int) as a std::chrono::microsecond() duration to your time_point.

Be aware that the <iomanip> functions std::ctime() and std::put_time() do not know about precision lower than a second. If you to print that level of precision, you'll need to write a function to do so.

#include <chrono>
#include <ctime>
#include <iomanip>
#include <iostream>

struct Tm : std::tm {
  int tm_usecs; // [0, 999999] micros after the sec

  Tm(const int year, const int month, const int mday, const int hour,
     const int min, const int sec, const int usecs, const int isDST = -1)
      : tm_usecs{usecs} {
    tm_year = year - 1900; // [0, 60] since 1900
    tm_mon = month - 1;    // [0, 11] since Jan
    tm_mday = mday;        // [1, 31]
    tm_hour = hour;        // [0, 23] since midnight
    tm_min = min;          // [0, 59] after the hour
    tm_sec = sec;          // [0, 60] after the min
                           //         allows for 1 positive leap second
    tm_isdst = isDST;      // [-1...] -1 for unknown, 0 for not DST,
                           //         any positive value if DST.
  }

  template <typename Clock_t = std::chrono::high_resolution_clock,
            typename MicroSecond_t = std::chrono::microseconds>
  auto to_time_point() -> typename Clock_t::time_point {
    auto time_c = mktime(this);
    return Clock_t::from_time_t(time_c) + MicroSecond_t{tm_usecs};
  }
};

int main() {
  using namespace std::chrono;

  auto tp_nomicro = Tm(2014, 8, 19, 14, 31, 23, 0).to_time_point();
  auto tp_micro = Tm(2014, 8, 19, 14, 31, 23, 946571).to_time_point();
  std::cout << duration_cast<microseconds>(tp_micro - tp_nomicro).count()
            << " microseconds apart.\n";

  auto time_c = high_resolution_clock::to_time_t(tp_micro);
  std::cout << std::ctime(&time_c) << '\n';
}
Snowhawk
  • 679
  • 7
  • 9
  • Thanks, this is exactly what I was looking for. I did notice however that LLVM does not have a from_time_t on the high_resolution_clock – bge0 Aug 20 '14 at 21:14
  • 1
    @bge0: The standard specifies `to_time_t` and `from_time_t` only for `system_clock`. `high_resolution_clock` is permitted (but not required) to be a type alias for `system_clock`. – Howard Hinnant Aug 21 '14 at 03:31