Extract Year/Month/Day etc. from Std::Chrono::Time_Point in C++

Extract year/month/day etc. from std::chrono::time_point in C++

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:

 system_clock::time_point now = system_clock::now();

You can then convert this to a time_t with:

time_t tt = system_clock::to_time_t(now);

Using the C library you can then convert a time_t to a tm, but you must choose whether you want that conversion to happen in the UTC timezone, or you local timezone:

tm utc_tm = *gmtime(&tt);
tm local_tm = *localtime(&tt);

Then you can print out the components of the tm, for example:

std::cout << local_tm.tm_year + 1900 << '\n';
std::cout << local_tm.tm_mon + 1 << '\n';
std::cout << local_tm.tm_mday << '\n';

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:

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

int
main()
{
using namespace std;
using namespace std::chrono;
typedef duration<int, ratio_multiply<hours::period, ratio<24> >::type> days;
system_clock::time_point now = system_clock::now();
system_clock::duration tp = now.time_since_epoch();
days d = duration_cast<days>(tp);
tp -= d;
hours h = duration_cast<hours>(tp);
tp -= h;
minutes m = duration_cast<minutes>(tp);
tp -= m;
seconds s = duration_cast<seconds>(tp);
tp -= s;
std::cout << d.count() << "d " << h.count() << ':'
<< m.count() << ':' << s.count();
std::cout << " " << tp.count() << "["
<< system_clock::duration::period::num << '/'
<< system_clock::duration::period::den << "]\n";

time_t tt = system_clock::to_time_t(now);
tm utc_tm = *gmtime(&tt);
tm local_tm = *localtime(&tt);
std::cout << utc_tm.tm_year + 1900 << '-';
std::cout << utc_tm.tm_mon + 1 << '-';
std::cout << utc_tm.tm_mday << ' ';
std::cout << utc_tm.tm_hour << ':';
std::cout << utc_tm.tm_min << ':';
std::cout << utc_tm.tm_sec << '\n';
}

It is handy to create a custom duration to model days:

typedef duration<int, ratio_multiply<hours::period, ratio<24> >::type> days;

Now you can get the time since the epoch, to as fine a precision as it can manage, with:

system_clock::duration tp = now.time_since_epoch();

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:

15806d 20:31:14 598155[1/1000000]
2013-4-11 20:31:14

My output indicates the system_clock::duration precision is microseconds. If desired, that can be truncated to milliseconds with:

milliseconds ms = duration_cast<milliseconds>(tp);

Update

This header-only C++11/14 library encapsulates the work above, reducing client work down to:

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

int
main()
{
// Reduce verbosity but let you know what is in what namespace
namespace C = std::chrono;
namespace D = date;
namespace S = std;

auto tp = C::system_clock::now(); // tp is a C::system_clock::time_point
{
// Need to reach into namespace date for this streaming operator
using namespace date;
S::cout << tp << '\n';
}
auto dp = D::floor<D::days>(tp); // dp is a sys_days, which is a
// type alias for a C::time_point
auto ymd = D::year_month_day{dp};
auto time = D::make_time(C::duration_cast<C::milliseconds>(tp-dp));
S::cout << "year = " << ymd.year() << '\n';
S::cout << "month = " << ymd.month() << '\n';
S::cout << "day = " << ymd.day() << '\n';
S::cout << "hour = " << time.hours().count() << "h\n";
S::cout << "minute = " << time.minutes().count() << "min\n";
S::cout << "second = " << time.seconds().count() << "s\n";
S::cout << "millisecond = " << time.subseconds().count() << "ms\n";
}

Which just output for me:

2015-07-10 20:10:36.023017
year = 2015
month = Jul
day = 10
hour = 20h
minute = 10min
second = 36s
millisecond = 23ms

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:

#include <chrono>

int
main()
{
using namespace std::chrono;
auto tp = system_clock::now();
auto dp = floor<days>(tp);
year_month_day ymd{dp};
hh_mm_ss time{floor<milliseconds>(tp-dp)};
auto y = ymd.year();
auto m = ymd.month();
auto d = ymd.day();
auto h = time.hours();
auto M = time.minutes();
auto s = time.seconds();
auto ms = time.subseconds();
}

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:

#include <chrono>

int
main()
{
using namespace std::chrono;
auto tp = zoned_time{current_zone(), system_clock::now()}.get_local_time();
auto dp = floor<days>(tp);
year_month_day ymd{dp};
hh_mm_ss time{floor<milliseconds>(tp-dp)};
auto y = ymd.year();
auto m = ymd.month();
auto d = ymd.day();
auto h = time.hours();
auto M = time.minutes();
auto s = time.seconds();
auto ms = time.subseconds();
}

The only difference above is the construction of tp which now has type local_time as opposed to sys_time in the UTC example. Alternatively one could have picked an arbitrary time zone with this small change:

auto tp = zoned_time{"Europe/London", system_clock::now()}.get_local_time();

Creating a `std::chrono::time_point` from a calendar date known at compile time

Yes, you can do the entire computation at compile time, creating a constexpr system_clock::time_point using Howard Hinnant's date/time library.

#include "date/date.h"
#include <chrono>

int
main()
{
using namespace date;
using namespace std::chrono;
constexpr system_clock::time_point tp = sys_days{January/9/2014} + 12h + 35min + 34s;
static_assert(tp == system_clock::time_point{1389270934s}, "");
}

This is assuming that the date/time is UTC. If it isn't, you will have to manually add/subtract the UTC offset to make it so. As time zone rules are changed at the whim of politicians all the time, there is little hope in making them constexpr. Even historical time zone rules are updated when misunderstandings come to light.

Also this program will port to C++20 by dropping #include "date/date.h" and using namespace date;. Also using Howard Hinnant's date/time library requires C++14 constexpr muscle. C++11 constexpr is not sufficient (but you can do it at run-time, dropping the constexpr and static_assert).

C++ Read time_t in to different timezone then extract year/month....nanoseconds

Since your input and output are in the same timezone, the timezone itself becomes irrelevant. This subsequently makes this problem very easy. One simply converts the count of nanoseconds into the desired fields. I recommend one short public domain helper function to convert the count of days into a {y, m, d} data structure.

#include <chrono>
#include <iostream>
#include <tuple>

// Returns year/month/day triple in civil calendar
// Preconditions: z is number of days since 1970-01-01 and is in the range:
// [numeric_limits<Int>::min(), numeric_limits<Int>::max()-719468].
template <class Int>
constexpr
std::tuple<Int, unsigned, unsigned>
civil_from_days(Int z) noexcept
{
static_assert(std::numeric_limits<unsigned>::digits >= 18,
"This algorithm has not been ported to a 16 bit unsigned integer");
static_assert(std::numeric_limits<Int>::digits >= 20,
"This algorithm has not been ported to a 16 bit signed integer");
z += 719468;
const Int era = (z >= 0 ? z : z - 146096) / 146097;
const unsigned doe = static_cast<unsigned>(z - era * 146097); // [0, 146096]
const unsigned yoe = (doe - doe/1460 + doe/36524 - doe/146096) / 365; // [0, 399]
const Int y = static_cast<Int>(yoe) + era * 400;
const unsigned doy = doe - (365*yoe + yoe/4 - yoe/100); // [0, 365]
const unsigned mp = (5*doy + 2)/153; // [0, 11]
const unsigned d = doy - (153*mp+2)/5 + 1; // [1, 31]
const unsigned m = mp + (mp < 10 ? 3 : -9); // [1, 12]
return std::tuple<Int, unsigned, unsigned>(y + (m <= 2), m, d);
}

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

auto nanosecondsSinceEpochTS = 1592130258959736008;
using days = duration<int, ratio_multiply<hours::period, ratio<24> >>;

nanoseconds ns(nanosecondsSinceEpochTS);
auto D = floor<days>(ns);
ns -= D;
auto H = duration_cast<hours>(ns);
ns -= H;
auto M = duration_cast<minutes>(ns);
ns -= M;
auto S = duration_cast<seconds>(ns);
ns -= S;
auto [y, m, d] = civil_from_days(D.count());
cout << "y = " << y << '\n';
cout << "m = " << m << '\n';
cout << "d = " << d << '\n';
cout << "H = " << H.count() << '\n';
cout << "M = " << M.count() << '\n';
cout << "S = " << S.count() << '\n';
cout << "NS = " << ns.count() << '\n';
}

Output:

y = 2020
m = 6
d = 14
H = 10
M = 24
S = 18
NS = 959736008

Update

After discussions in the comments below, it was discovered that nanosecondsSinceEpochTS is UTC, not America/Chicago as I presumed. That means that the UTC offset, which is a function of both the timezone and the nanosecond count, must be added to the count as the first step. And then proceed as directed above to get each field.

Finding the correct offset is a non-trivial procedure which I won't attempt to show code for. One technique is to precompute a table of {utc_timestamp, utc_offset} for all of the input years in question, and then use the input utc_timestamp to look up the correct offset.

In C++20 one can simply:

zoned_time zt{"America/Chicago", sys_time{nanoseconds{nanosecondsSinceEpochTS}}};
cout << zt << '\n';

And get the output:

2020-06-14 05:24:18.959736008 CDT

If one wants the integral fields:

auto lt = zt.get_local_time();  // look up utc offset and add it to sys_time
year_month_day ymd{floor<days>(lt)}; // run civil_from_days
hh_mm_ss tod{lt - floor<days>(lt)}; // {H, M, S, NS} since local midnight

// copy each underlying integral value
auto y = int{ymd.year()};
auto m = unsigned{ymd.month()};
auto d = unsigned{ymd.day()};
auto H = tod.hours().count();
auto M = tod.minutes().count();
auto S = tod.seconds().count();
auto NS = tod.subseconds().count();

Disclaimer: As I write this, no vendor is yet shipping this part of C++20.

Update for POSIX time zones

If you're willing to use this free, open-source, header-only library you can use POSIX time zones which avoid the IANA database install issues.

It looks like:

#include "date/ptz.h"
#include <iostream>

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

auto nanosecondsSinceEpochTS = 1592130258959736008;
zoned_time zt{Posix::time_zone{"CST6CDT,M3.2.0,M11.1.0"},
sys_time<nanoseconds>{nanoseconds{nanosecondsSinceEpochTS}}};
cout << zt << '\n';
}

which outputs:

2020-06-14 05:24:18.959736008 CDT

Note that this only models America/Chicago back to 2007. Prior to 2007 America/Chicago had different daylight saving rules.

Show day, month and year with std::chrono?

std::put_time is what you need:

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

int main()
{
auto now = std::chrono::system_clock::to_time_t(std::chrono::system_clock::now());
std::cout << std::put_time(std::localtime(&now), "%Y-%m-%d") << "\n";
}

Prints:

2018-05-14

How to convert std::chrono::time_point to calendar datetime string with fractional seconds?

If system_clock, this class have time_t conversion.

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

using namespace std::chrono;

int main()
{
system_clock::time_point p = system_clock::now();

std::time_t t = system_clock::to_time_t(p);
std::cout << std::ctime(&t) << std::endl; // for example : Tue Sep 27 14:21:13 2011
}

example result:

Thu Oct 11 19:10:24 2012

EDIT:
But, time_t does not contain fractional seconds.
Alternative way is to use time_point::time_since_epoch() function. This function returns duration from epoch.
Follow example is milli second resolution's fractional.

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

using namespace std::chrono;

int main()
{
high_resolution_clock::time_point p = high_resolution_clock::now();

milliseconds ms = duration_cast<milliseconds>(p.time_since_epoch());

seconds s = duration_cast<seconds>(ms);
std::time_t t = s.count();
std::size_t fractional_seconds = ms.count() % 1000;

std::cout << std::ctime(&t) << std::endl;
std::cout << fractional_seconds << std::endl;
}

example result:

Thu Oct 11 19:10:24 2012

925

Get current number of hours and minutes using chrono::time_point

The problem is that there isn't really any such functionality in the standard library. You have to convert the time point to a time_t and use the old functions to get a tm structure.

C++ Print days, hours, minutes, etc. of a chrono::duration

When you cannot use fmtlib or wait for C++20 <format>, you at least want to delay the invocation of cout() on durations as much as possible. Also, let <chrono> handle the computation for you. Both measures improve the terseness of the snippet:

const auto hrs = duration_cast<hours>(sysInactive);
const auto mins = duration_cast<minutes>(sysInactive - hrs);
const auto secs = duration_cast<seconds>(sysInactive - hrs - mins);
const auto ms = duration_cast<milliseconds>(sysInactive - hrs - mins - secs);

And the output:

cout << "System inactive for " << hrs.count() <<
":" << mins.count() <<
":" << secs.count() <<
"." << ms.count() << endl;

Note that you could also define a utility template,

template <class Rep, std::intmax_t num, std::intmax_t denom>
auto chronoBurst(std::chrono::duration<Rep, std::ratio<num, denom>> d)
{
const auto hrs = duration_cast<hours>(d);
const auto mins = duration_cast<minutes>(d - hrs);
const auto secs = duration_cast<seconds>(d - hrs - mins);
const auto ms = duration_cast<milliseconds>(d - hrs - mins - secs);

return std::make_tuple(hrs, mins, secs, ms);
}

that has a nice use case in conjunction with structured bindings:

const auto [hrs, mins, secs, ms] = chronoBurst(sysInactive);

obtain current day using chrono

Chronos doesn't extend to date issues; that's really not it's purpose. The dividing line between date issues and "time" issues is the day. And Chrono doesn't define a day type.

But you could just divide the time in hours by 24. Or even better, define your own duration typedef and use that:

typedef std::duration<std::uint32_t, std::ratio<3600 * 24>> day;

Then just use a time_point with that type:

std::chrono::time_point<std::chrono::system_clock, day> day_getter;
day_getter.time_since_epoch();


Related Topics



Leave a reply



Submit