简体   繁体   中英

fully separated date with milliseconds from std::chrono::system_clock

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? ie 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?

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( );

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

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?

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 .

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM