4

My current pattern (for unix) is to call gettimeofday, cast the tv_sec field to a time_t, pass that through localtime, and combine the results with tv_usec. That gives me a full date (year, month, day, hour, minute, second, nanoseconds).

I'm trying to update my code to C++11 for portability and general good practice. I'm able to do the following:

auto currentTime = std::chrono::system_clock::now( );
const time_t time = std::chrono::system_clock::to_time_t( currentTime );
const tm *values = localtime( &time );
// read values->tm_year, etc.

But I'm stuck on the milliseconds/nanoseconds. For one thing, to_time_t claims that rounding is implementation defined (!) so I don't know if a final reading of 22.6 seconds should actually be 21.6, and for another I don't know how to get the number of milliseconds since the previous second (are seconds guaranteed by the standard to be regular? i.e. could I get the total milliseconds since the epoch and just modulo it? Even if that is OK it feels ugly).

How should I get the current date from std::chrono::system_clock with milliseconds?

Dave
  • 44,275
  • 12
  • 65
  • 105
  • _If std::time_t has lower precision, it is implementation-defined whether the value is **rounded** or **truncated**._ In either case, 22.6 will never be 21.6, so don't worry about that. – Tom Knapen Jun 30 '13 at 09:09
  • 1
    @TomKnapen but 21.6 *can* become 22.6, if it is rounded instead of truncated for the seconds. Maybe my wording wasn't the best but that's what I was getting at. – Dave Jun 30 '13 at 10:34
  • In my experience, rounding 21.6 equals 22, and truncating 21.6 equals 21. So either way, unless you are adding the 'fractional' part yourself, there is no way 21.6 can ever possibly become 22.6. – Tom Knapen Jun 30 '13 at 11:23
  • @TomKnapen but the point is that I *am* adding the fractional part myself; I would be getting the seconds from the (rounded or truncated) `time_t` value, and the fractional part from the original clock. – Dave Jun 30 '13 at 12:00

4 Answers4

2

I realised that I can use from_time_t to get a "rounded" value, and check which type of rounding occurred. This also doesn't rely on every second being exactly 1000 milliseconds, and works with out-of-the-box C++11:

const auto currentTime = std::chrono::system_clock::now( );
time_t time = std::chrono::system_clock::to_time_t( currentTime );
auto currentTimeRounded = std::chrono::system_clock::from_time_t( time );
if( currentTimeRounded > currentTime ) {
    -- time;
    currentTimeRounded -= std::chrono::seconds( 1 );
}
const tm *values = localtime( &time );
int year = values->tm_year + 1900;
// etc.
int milliseconds = std::chrono::duration_cast<std::chrono::duration<int,std::milli> >( currentTime - currentTimeRounded ).count( );
Dave
  • 44,275
  • 12
  • 65
  • 105
2

Instead of using to_time_t which rounds off you can instead do like this

auto tp = std::system_clock::now();
auto s = std::chrono::duration_cast<std::chrono::seconds>(tp.time_since_epoch());
auto t = (time_t)(s.count());

That way you get the seconds without the round-off. It is more effective than checking difference between to_time_t and from_time_t.

AndersK
  • 35,813
  • 6
  • 60
  • 86
2

Using this free, open-source library you can get the local time with millisecond precision like this:

#include "tz.h"
#include <iostream>

int
main()
{
    using namespace date;
    using namespace std::chrono;
    std::cout << make_zoned(current_zone(),
                            floor<milliseconds>(system_clock::now())) << '\n';
}

This just output for me:

2016-09-06 12:35:09.102 EDT

make_zoned is a factory function that creates a zoned_time<milliseconds>. The factory function deduces the desired precision for you. A zoned_time is a pairing of a time_zone and a local_time. You can get the local time out with:

local_time<milliseconds> lt = zt.get_local_time();

local_time is a chrono::time_point. You can break this down into date and time field types if you want like this:

auto zt = make_zoned(current_zone(), floor<milliseconds>(system_clock::now()));
auto lt = zt.get_local_time();
local_days ld = floor<days>(lt);          // local time truncated to days
year_month_day ymd{ld};                   // {year, month, day}
time_of_day<milliseconds> time{lt - ld};  // {hours, minutes, seconds, milliseconds}
// auto time = make_time(lt - ld);        // another way to create time_of_day
auto y = ymd.year();         // 2016_y
auto m = ymd.month();        // sep
auto d = ymd.day();          // 6_d
auto h = time.hours();       // 12h
auto min = time.minutes();   // 35min
auto s = time.seconds();     // 9s
auto ms = time.subseconds(); // 102ms
Howard Hinnant
  • 206,506
  • 52
  • 449
  • 577
0

I read the standard like this:

It is implementation defined whether the value is rounder or truncated, but naturally the rounding or truncation only occurs on the most detailed part of the resulting time_t. That is: the combined information you get from time_t is never more wrong than 0.5 of its granularity.

If time_t on your system only supported seconds, you would be right that there could be 0.5 seconds systematic uncertainty (unless you find out how things were implemented).

tv_usec is not standard C++, but an accessor of time_t on posix. To conclude, you should not expect any rounding effects bigger than half of the smallest time value difference your system supports, so certainly not more than 0.5 micro seconds.

The most straight forward way is to use boost ptime. It has methods such as fractional_seconds() http://www.boost.org/doc/libs/1_53_0/doc/html/date_time/posix_time.html#date_time.posix_time.ptime_class

For interop with std::chrono, you can convert as described here: https://stackoverflow.com/a/4918873/1149664

Or, have a look at this question: How to convert std::chrono::time_point to calendar datetime string with fractional seconds?

Community
  • 1
  • 1
Johan Lundberg
  • 26,184
  • 12
  • 71
  • 97
  • I'm actually getting the `tv_usec` value directly from `gettimeofday`'s struct, before it's passed through a `time_t`, which I believe is safe. I thought `time_t` only holds seconds, so I'm expecting the 0.5 second difference rather than 0.5 microseconds, though I haven't tested (not sure that I can since it's implementation defined). As for the boost answer; I'll have to take a moment to understand what it's doing, but I try to avoid boost where possible, and stick to just the stuff which becomes standard. – Dave Jun 30 '13 at 12:12
  • OK I see what it's doing; you write the time to a stream and read it back to effectively "floor" it, then get the difference. I can see the logic, but it seems horribly convoluted; converting numbers to/from strings just to compare them is something I always avoid. There must be a way to do this without manipulating strings/streams… – Dave Jun 30 '13 at 12:20
  • No, that is not the point, and not what I meant. The stream is just to show the result, and you get back what you put in. What exactly would you like to do with the times? In either case the boost time libraries are the place to look. – Johan Lundberg Jun 30 '13 at 12:55
  • then I've misunderstood; I want to write something like "HH:MM:SS.xxxx". It seems that the "diff" in your code would give me the ".xxxx" bit, but you write to a stream then read back from it to get that variable. – Dave Jun 30 '13 at 15:49