122

I need to convert std::chrono::time_point to and from a long type (integer 64 bits). I´m starting working with std::chrono ...

Here is my code:

int main ()
{
     std::chrono::time_point<std::chrono::system_clock> now = std::chrono::system_clock::now();

    auto epoch = now.time_since_epoch();
    auto value = std::chrono::duration_cast<std::chrono::milliseconds>(epoch);
    long duration = value.count();


    std::chrono::duration<long> dur(duration);

    std::chrono::time_point<std::chrono::system_clock> dt(dur);

    if (dt != now)
        std::cout << "Failure." << std::endl;
    else
        std::cout << "Success." << std::endl;
}

This code compiles, but does not show success.

Why is dt different than now at the end?

What is missing on that code?

user438383
  • 5,716
  • 8
  • 28
  • 43
Mendes
  • 17,489
  • 35
  • 150
  • 263
  • 1
    I'm not too familiar with the chrono library, but I believe you have to use `std::chrono::duration dur` and even then, you might get rounding errors (`std::chrono::system_clock` has probably a higher resolution than milliseconds). – MikeMB Jul 06 '15 at 22:41
  • @MikeMB all clocks on reasonably new hardware should have somewhere around microsecond/sub-microsecond precision anyway. See an article titled [The Three Clocks](https://www.google.com/url?sa=t&rct=j&q=&esrc=s&source=web&cd=&cad=rja&uact=8&ved=2ahUKEwi_tP7xs9DpAhV8ITQIHamjAxIQFjAAegQIBhAB&url=https%3A%2F%2Fwww.modernescpp.com%2Findex.php%2Fthe-three-clocks&usg=AOvVaw2E-RgmLWttMk7B9jLbmt39). Really, windows will usually have higher precision with `system_clock` (around 100 times greater), but even this is usually sub-microsecond. – Werlious May 26 '20 at 01:47

4 Answers4

225
std::chrono::time_point<std::chrono::system_clock> now = std::chrono::system_clock::now();

This is a great place for auto:

auto now = std::chrono::system_clock::now();

Since you want to traffic at millisecond precision, it would be good to go ahead and covert to it in the time_point:

auto now_ms = std::chrono::time_point_cast<std::chrono::milliseconds>(now);

now_ms is a time_point, based on system_clock, but with the precision of milliseconds instead of whatever precision your system_clock has.

auto epoch = now_ms.time_since_epoch();

epoch now has type std::chrono::milliseconds. And this next statement becomes essentially a no-op (simply makes a copy and does not make a conversion):

auto value = std::chrono::duration_cast<std::chrono::milliseconds>(epoch);

Here:

long duration = value.count();

In both your and my code, duration holds the number of milliseconds since the epoch of system_clock.

This:

std::chrono::duration<long> dur(duration);

Creates a duration represented with a long, and a precision of seconds. This effectively reinterpret_casts the milliseconds held in value to seconds. It is a logic error. The correct code would look like:

std::chrono::milliseconds dur(duration);

This line:

std::chrono::time_point<std::chrono::system_clock> dt(dur);

creates a time_point based on system_clock, with the capability of holding a precision to the system_clock's native precision (typically finer than milliseconds). However the run-time value will correctly reflect that an integral number of milliseconds are held (assuming my correction on the type of dur).

Even with the correction, this test will (nearly always) fail though:

if (dt != now)

Because dt holds an integral number of milliseconds, but now holds an integral number of ticks finer than a millisecond (e.g. microseconds or nanoseconds). Thus only on the rare chance that system_clock::now() returned an integral number of milliseconds would the test pass.

But you can instead:

if (dt != now_ms)

And you will now get your expected result reliably.

Putting it all together:

int main ()
{
    auto now = std::chrono::system_clock::now();
    auto now_ms = std::chrono::time_point_cast<std::chrono::milliseconds>(now);

    auto value = now_ms.time_since_epoch();
    long duration = value.count();

    std::chrono::milliseconds dur(duration);

    std::chrono::time_point<std::chrono::system_clock> dt(dur);

    if (dt != now_ms)
        std::cout << "Failure." << std::endl;
    else
        std::cout << "Success." << std::endl;
}

Personally I find all the std::chrono overly verbose and so I would code it as:

int main ()
{
    using namespace std::chrono;
    auto now = system_clock::now();
    auto now_ms = time_point_cast<milliseconds>(now);

    auto value = now_ms.time_since_epoch();
    long duration = value.count();

    milliseconds dur(duration);

    time_point<system_clock> dt(dur);

    if (dt != now_ms)
        std::cout << "Failure." << std::endl;
    else
        std::cout << "Success." << std::endl;
}

Which will reliably output:

Success.

Finally, I recommend eliminating temporaries to reduce the code converting between time_point and integral type to a minimum. These conversions are dangerous, and so the less code you write manipulating the bare integral type the better:

int main ()
{
    using namespace std::chrono;
    // Get current time with precision of milliseconds
    auto now = time_point_cast<milliseconds>(system_clock::now());
    // sys_milliseconds is type time_point<system_clock, milliseconds>
    using sys_milliseconds = decltype(now);
    // Convert time_point to signed integral type
    auto integral_duration = now.time_since_epoch().count();
    // Convert signed integral type to time_point
    sys_milliseconds dt{milliseconds{integral_duration}};
    // test
    if (dt != now)
        std::cout << "Failure." << std::endl;
    else
        std::cout << "Success." << std::endl;
}

The main danger above is not interpreting integral_duration as milliseconds on the way back to a time_point. One possible way to mitigate that risk is to write:

    sys_milliseconds dt{sys_milliseconds::duration{integral_duration}};

This reduces risk down to just making sure you use sys_milliseconds on the way out, and in the two places on the way back in.

And one more example: Let's say you want to convert to and from an integral which represents whatever duration system_clock supports (microseconds, 10th of microseconds or nanoseconds). Then you don't have to worry about specifying milliseconds as above. The code simplifies to:

int main ()
{
    using namespace std::chrono;
    // Get current time with native precision
    auto now = system_clock::now();
    // Convert time_point to signed integral type
    auto integral_duration = now.time_since_epoch().count();
    // Convert signed integral type to time_point
    system_clock::time_point dt{system_clock::duration{integral_duration}};
    // test
    if (dt != now)
        std::cout << "Failure." << std::endl;
    else
        std::cout << "Success." << std::endl;
}

This works, but if you run half the conversion (out to integral) on one platform and the other half (in from integral) on another platform, you run the risk that system_clock::duration will have different precisions for the two conversions.

ulidtko
  • 14,740
  • 10
  • 56
  • 88
Howard Hinnant
  • 206,506
  • 52
  • 449
  • 577
  • 1
    @BrentNash: Careful, I might take you up on that. ;-) I'll be at Cppcon 2015 (http://cppcon.org) talking about this: http://howardhinnant.github.io/date_v2.html . The subject is very closely related to the `time_point_cast(now)` in my answer above. Only the duration is courser: `time_point_cast(now)`. – Howard Hinnant Jul 07 '15 at 19:57
  • Typo in last comment: coarser, not courser. Great answer though. – Dirk Eddelbuettel Dec 07 '16 at 18:14
  • 2
    The problem with English is that it isn't parsable with a C++ compiler, and the compiler makes a better spell checker. :-) – Howard Hinnant Dec 07 '16 at 19:23
  • 7
    This is very helpful although I think it would have been a tab bit better if you had not been using 'auto' everywhere. Just for lazy people like me who would like to see what types are being manipulated without having to look elsewhere. Thanks a bunch @HowardHinnant – MarkoPaulo Oct 04 '17 at 17:38
  • Just an observation: using `auto` in an example diminishes the value of the example a lot. – Sampath Jul 02 '21 at 03:32
  • You should have just lead with "count()". Really. This is some real TL;DR just to get a bloody integral value. Also, as others have said, using auto diminishes the example, as others have said. IMO if one has to resort to auto very much then quite likely the library is too complex for the problem at hand. Much of what I've seen with std::chrono and co., it is just trafficking in a counter and a ratio. Why are these concepts being used in templates? Seems like overkill, more of the Committee's fetish for over-complicating stuff. – Eric M Sep 08 '21 at 17:25
  • 1
    To go from `system_clock::now()` to `ms` since 1970 in a `long`: `long i = system_clock::now().time_since_epoch()/1ms;` – Howard Hinnant Sep 08 '21 at 17:25
  • `.count()` works too, if you throw in a `duration_cast`. That being said, the compiler will catch logic errors for at compile-time if you stay within the chrono type system for as long as possible. For example: https://stackoverflow.com/a/59446610/576911 – Howard Hinnant Sep 08 '21 at 17:28
  • Yeah, sorry, I thought better of my O.P. didn't expect you to be answering it right away. – Eric M Sep 08 '21 at 17:29
  • I hate it when I'm everywhere too. :-) Hopefully helpful chrono video tutorial: https://www.youtube.com/watch?v=P32hvk8b13M It costs an hour. – Howard Hinnant Sep 08 '21 at 17:31
  • IDK, I dunno. I see value in this stuff for locales. But if I am dealing in stuff like time since the UNIX epoch, this is like using a nuclear bomb when a bullet would do as well. – Eric M Sep 08 '21 at 17:33
  • Some value a strong type system more than others. Fwiw, this doesn't have much to do with locales, except maybe for converting localized strings into weekdays and months. And C++11/14/17 chrono really doesn't help there anyway (it does in C++20). – Howard Hinnant Sep 08 '21 at 17:37
  • Well, how many seconds or mS since the epoch *is* actually just a integer, eh? – Eric M Sep 08 '21 at 17:40
  • The locale stuff though can be really hard to handle in older versions of C++, your stuff here for that is quite nice. – Eric M Sep 08 '21 at 17:41
  • Two links highly recommended: https://stackoverflow.com/a/59446610/576911 and https://www.youtube.com/watch?v=P32hvk8b13M – Howard Hinnant Sep 08 '21 at 17:44
  • @HowardHinnant Good answer! What's `std::chrono::time_point dt(dur);` in your first example?If I understand you correctly,the `dt` is almost the same with `now`. Am I right? If I miss something, please let me know. – John Jun 23 '22 at 01:12
  • `dt` has the same type as `now`, but with a value truncated to milliseconds precision. In this case the truncation is towards the epoch, which is in the past, so truncated to the beginning of the millisecond `now` is within. – Howard Hinnant Jun 23 '22 at 13:14
11

I would also note there are two ways to get the number of ms in the time point. I'm not sure which one is better, I've benchmarked them and they both have the same performance, so I guess it's a matter of preference. Perhaps Howard could chime in:

auto now = system_clock::now();

//Cast the time point to ms, then get its duration, then get the duration's count.
auto ms = time_point_cast<milliseconds>(now).time_since_epoch().count();

//Get the time point's duration, then cast to ms, then get its count.
auto ms = duration_cast<milliseconds>(tpBid.time_since_epoch()).count();

The first one reads more clearly in my mind going from left to right.

Feem
  • 379
  • 4
  • 9
8

as a single line:

long value_ms = std::chrono::duration_cast<std::chrono::milliseconds>(std::chrono::time_point_cast<std::chrono::milliseconds>(std::chrono::high_resolution_clock::now()).time_since_epoch()).count();
Harald P.
  • 89
  • 1
  • 2
0

time_point objects only support arithmetic with other time_point or duration objects.

You'll need to convert your long to a duration of specified units, then your code should work correctly.

Mr. Llama
  • 20,202
  • 2
  • 62
  • 115