Answer updated with better algorithms, link to detailed description of the algorithms, and complete conversion to std::tm
.
I would like to print or extract year/month/day values.
Is there a simple way to convert from time_point to tm (preferably
without boost)?
The first thing to note is that std::chrono::time_point
is templated not only on duration
, but also on the clock. The clock implies an epoch. And different clocks can have different epochs.
For example, on my system, std::chrono::high_resolution_clock
and std::chrono::steady_clock
have an epoch of: whenever the computer booted up. If you don't know what time the computer booted up, there is no way to convert that time_point
to any calendar system.
That being said, you were probably talking just about std::chrono::system_clock::time_point
, as this time_point
, and only this time_point
, is required to have a deterministic relationship with the civil (gregorian) calendar.
As it turns out, every implementation of std::chrono::system_clock
I'm aware of is using unix time. This has an epoch of New Years 1970 neglecting leap seconds.
This isn't guaranteed by the standard. However you can take advantage of this fact if you want to with the following formulas found at:
chrono-Compatible Low-Level Date Algorithms
First off, warning, I'm using the latest C++1y draft, which includes great new constexpr
tools. If you need to back off some of the constexpr
attributes for your compiler, just do so.
Given the algorithms found at the above link, you can can convert a std::chrono::time_point<std::chrono::system_clock, Duration>
to a std::tm
, without using time_t
with the following function:
template <class Duration>
std::tm
make_utc_tm(std::chrono::time_point<std::chrono::system_clock, Duration> tp)
{
using namespace std;
using namespace std::chrono;
typedef duration<int, ratio_multiply<hours::period, ratio<24>>> days;
// t is time duration since 1970-01-01
Duration t = tp.time_since_epoch();
// d is days since 1970-01-01
days d = round_down<days>(t);
// t is now time duration since midnight of day d
t -= d;
// break d down into year/month/day
int year;
unsigned month;
unsigned day;
std::tie(year, month, day) = civil_from_days(d.count());
// start filling in the tm with calendar info
std::tm tm = {0};
tm.tm_year = year - 1900;
tm.tm_mon = month - 1;
tm.tm_mday = day;
tm.tm_wday = weekday_from_days(d.count());
tm.tm_yday = d.count() - days_from_civil(year, 1, 1);
// Fill in the time
tm.tm_hour = duration_cast<hours>(t).count();
t -= hours(tm.tm_hour);
tm.tm_min = duration_cast<minutes>(t).count();
t -= minutes(tm.tm_min);
tm.tm_sec = duration_cast<seconds>(t).count();
return tm;
}
Also note that the std::chrono::system_clock::time_point
on all existing implementations is a duration in the UTC (neglecting leap seconds) time zone. If you want to convert the time_point
using another timezone, you will need to add/subtract the duration offset of the timezone to the std::chrono::system_clock::time_point
prior to converting it to a precision of days
. And if you further want to take leap seconds into account, then adjust by the appropriate number of seconds prior to truncation to days
using this table, and the knowledge that unix time is aligned with UTC now.
This function can be verified with:
#include <iostream>
#include <iomanip>
void
print_tm(const std::tm& tm)
{
using namespace std;
cout << tm.tm_year+1900;
char fill = cout.fill();
cout << setfill('0');
cout << '-' << setw(2) << tm.tm_mon+1;
cout << '-' << setw(2) << tm.tm_mday;
cout << ' ';
switch (tm.tm_wday)
{
case 0:
cout << "Sun";
break;
case 1:
cout << "Mon";
break;
case 2:
cout << "Tue";
break;
case 3:
cout << "Wed";
break;
case 4:
cout << "Thu";
break;
case 5:
cout << "Fri";
break;
case 6:
cout << "Sat";
break;
}
cout << ' ';
cout << ' ' << setw(2) << tm.tm_hour;
cout << ':' << setw(2) << tm.tm_min;
cout << ':' << setw(2) << tm.tm_sec << " UTC.";
cout << setfill(fill);
cout << " This is " << tm.tm_yday << " days since Jan 1\n";
}
int
main()
{
print_tm(make_utc_tm(std::chrono::system_clock::now()));
}
Which for me currently prints out:
2013-09-15 Sun 18:16:50 UTC. This is 257 days since Jan 1
In case chrono-Compatible Low-Level Date Algorithms goes offline, or gets moved, here are the algorithms used in make_utc_tm
. There are in-depth explanations of these algorithms at the above link. They are well-tested, and have an extraordinarily large range of validity.
// Returns number of days since civil 1970-01-01. Negative values indicate
// days prior to 1970-01-01.
// Preconditions: y-m-d represents a date in the civil (Gregorian) calendar
// m is in [1, 12]
// d is in [1, last_day_of_month(y, m)]
// y is "approximately" in
// [numeric_limits<Int>::min()/366, numeric_limits<Int>::max()/366]
// Exact range of validity is:
// [civil_from_days(numeric_limits<Int>::min()),
// civil_from_days(numeric_limits<Int>::max()-719468)]
template <class Int>
constexpr
Int
days_from_civil(Int y, unsigned m, unsigned d) 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");
y -= m <= 2;
const Int era = (y >= 0 ? y : y-399) / 400;
const unsigned yoe = static_cast<unsigned>(y - era * 400); // [0, 399]
const unsigned doy = (153*(m + (m > 2 ? -3 : 9)) + 2)/5 + d-1; // [0, 365]
const unsigned doe = yoe * 365 + yoe/4 - yoe/100 + doy; // [0, 146096]
return era * 146097 + static_cast<Int>(doe) - 719468;
}
// 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);
}
template <class Int>
constexpr
unsigned
weekday_from_days(Int z) noexcept
{
return static_cast<unsigned>(z >= -4 ? (z+4) % 7 : (z+5) % 7 + 6);
}
template <class To, class Rep, class Period>
To
round_down(const std::chrono::duration<Rep, Period>& d)
{
To t = std::chrono::duration_cast<To>(d);
if (t > d)
--t;
return t;
}
Update
More recently I have wrapped the above algorithms up into a freely available date/time library documented and available here. This library makes it very easy to extract a year/month/day from std::system_clock::time_point
, and even hours:minutes:seconds:fractional-seconds. And all without going through time_t
.
Here is a simple program using the above header-only library to print out the current date and time in the UTC timezone, to the precision of whatever system_clock::time_point
offers (in this case microseconds):
#include "date.h"
#include <iostream>
int
main()
{
using namespace date;
using namespace std;
using namespace std::chrono;
auto const now = system_clock::now();
auto const dp = time_point_cast<days>(now);
auto const date = year_month_day(dp);
auto const time = make_time(now-dp);
cout << date << ' ' << time << " UTC\n";
}
Which just output for me:
2015-05-19 15:03:47.754002 UTC
This library effectively turns std::chrono::system_clock::time_point
into an easy-to-use date-time type.
time_t
is that all of the standard functions for convertingtime_t
totm
are not thread-safe. – Saratime_t t = std::chrono::system_clock::to_time_t(std::chrono::system_clock::now());
– Bawdy