How can I extract the year, month, day, hour, minute, second and millisecond from an std::chrono::time_point
object?
I only saw examples on how to extract the total amount of e.g. seconds from a duration
.
c++c++-chronoc++11time
How can I extract the year, month, day, hour, minute, second and millisecond from an std::chrono::time_point
object?
I only saw examples on how to extract the total amount of e.g. seconds from a duration
.
A unix time stamp is defined as the number of seconds since January 1, 1970 UTC, except not counting all the seconds. This is somewhat ridiculous and one has to wonder what the point of it is, so I agree that this is a silly question.
Anyway, lets look at some platform documentation for time_t
and time()
.
time() returns the time as the number of seconds since the Epoch, 1970-01-01 00:00:00 +0000 (UTC).
POSIX.1 defines seconds since the Epoch using a formula that approximates the number of seconds between a specified time and the Epoch. This formula takes account of the facts that all years that are evenly divisible by 4 are leap years, but years that are evenly divisible by 100 are not leap years unless they are also evenly divisible by 400, in which case they are leap years. This value is not the same as the actual number of seconds between the time and the Epoch, because of leap seconds and because system clocks are not required to be synchronized to a standard reference. The intention is that the interpretation of seconds since the Epoch values be consistent; see POSIX.1-2008 Rationale A.4.15 for further rationale.
The time function returns the number of seconds elapsed since midnight (00:00:00), January 1, 1970, Coordinated Universal Time (UTC), according to the system clock.
The functions ctime(), gmtime(), and localtime() all take as an argument a time value representing the time in seconds since the Epoch (00:00:00 UTC, January 1, 1970;
The asctime(), ctime(), difftime(), gmtime(), localtime(), and mktime() functions conform to ISO/IEC 9899:1990 (
ISO C90''), and conform to ISO/IEC 9945-1:1996 (
POSIX.1'') provided the selected local timezone does not contain a leap-second table (see zic(8)).
Similar documentation can be found for other systems, such as AIX, HP-UX, Solaris, etc.
So although not specified in C++ there is an easy and widely portable way to get a Unix timestamp:
auto unix_timestamp = std::chrono::seconds(std::time(NULL));
And if you want a number of milliseconds since 1 Jan 1970 UTC (similarly not counting all of them) then you can do this:
int unix_timestamp_x_1000 = std::chrono::milliseconds(unix_timestamp).count();
Just remember that these values aren't real times, so you can't in general use unix timestamps in arithmetic. For example subtracting unix timestamps does not give you an accurate count of seconds between the times. Or if you did something like:
std::chrono::steady_clock::now() - unix_timestamp;
you would not get a time point actually corresponding to 1970-01-01 00:00:00+0000.
As Andy Prowl suggests you could do something silly like:
// 1 Jan 1970 (no time zone)
std::tm c = { 0, 0, 0, 1, 0, 70, 0, 0, -1};
// treat it as 1 Jan 1970 (your system's time zone) and get the
// number of seconds since your system's epoch (leap seconds may
// or may not be included)
std::time_t l = std::mktime(&c);
// get a calender time for that time_point in UTC. When interpreted
// as UTC this represents the same calendar date and time as the
// original, but if we change the timezone to the system TZ then it
// represents a time offset from the original calendar time by as
// much as UTC differs from the local timezone.
std::tm m = *std::gmtime(&l);
// Treat the new calendar time as offset time in the local TZ. Get
// the number of seconds since the system epoch (again, leap seconds
// may or may not be counted).
std::time_t n = std::mktime(&m);
l -= (n-l); // subtract the difference
l
should now represent the (wrong) number of seconds since 1 Jan 1970 UTC. As long as there are no leap seconds between the system epoch and 1 Jan 1970 (system time zone), or within an equal amount of time in the other direction from the system epoch, then any counted leap seconds should cancel out and l
will be wrong in just the way that unix timestamps are wrong.
Another option is to use a decent date library such as Howard Hinnant's chrono::date
. (Howard Hinnant was one of the guys that worked on the C++11 <chrono>
library.)
auto now = system_clock::now();
sys_days today = time_point_cast<days>(now);
system_clock::time_point this_morning = today;
sys_days unix_epoch = day(1)/jan/1970;
days days_since_epoch = today - unix_epoch;
auto s = now - this_morning;
auto tz_offset = hours(0);
int unix_timestamp = (days_since_epoch + s + tz_offset) / seconds(1);
If you want to handle leap seconds Howard Hinnant also provides a library that includes facilities for handling them as well as for parsing time zone databases as the source for leap second data.
std::tm tm = {};
std::stringstream ss("Jan 9 2014 12:35:34");
ss >> std::get_time(&tm, "%b %d %Y %H:%M:%S");
auto tp = std::chrono::system_clock::from_time_t(std::mktime(&tm));
GCC prior to version 5 doesn't implement std::get_time
. You should also be able to write:
std::tm tm = {};
strptime("Thu Jan 9 2014 12:35:34", "%a %b %d %Y %H:%M:%S", &tm);
auto tp = std::chrono::system_clock::from_time_t(std::mktime(&tm));
Best Answer
You can only extract this information from a
system_clock::time_point
. This is the only system-supplied clock that has a relationship with the civil calendar. Here is how to get the current time_point using this clock:You can then convert this to a
time_t
with:Using the C library you can then convert a
time_t
to atm
, but you must choose whether you want that conversion to happen in the UTC timezone, or you local timezone:Then you can print out the components of the tm, for example:
Additionally
If you want, you can take advantage of this non-guaranteed information:
Every implementation of
system_clock
I'm aware of is based on unix time. I.e. the number of seconds since New Years 1970 UTC, neglecting leap seconds. And the precision of this count is usually finer than seconds. Here is a complete program which extracts all of this information:It is handy to create a custom
duration
to model days:Now you can get the time since the epoch, to as fine a precision as it can manage, with:
Then truncate it to days, and subtract that off.
Then truncate it to hours, and subtract that off.
Continue until you've subtracted off the seconds.
What you're left with is the fraction of a second with the units of
system_clock::duration
. So print out that run time value and the compile time units of that value as shown.For me this program prints out:
My output indicates the
system_clock::duration
precision is microseconds. If desired, that can be truncated to milliseconds with:Update
This header-only C++11/14 library encapsulates the work above, reducing client work down to:
Which just output for me:
Another Update
This library grew into a C++ standards proposal and is now in the C++20 working draft. The syntax for extracting these fields from a
system_clock::time_point
in C++20 will be:The above assumes you want these fields in UTC. If you prefer them in some other time zone, that will also be possible. For example, here is how to do it in your computer's current local time zone:
The only difference above is the construction of
tp
which now has typelocal_time
as opposed tosys_time
in the UTC example. Alternatively one could have picked an arbitrary time zone with this small change: