If I do the following:
auto time = std::chrono::system_clock::duration::zero();
std::cout << time.count() << std::endl;
time += std::chrono::years(2024 - 1970);
std::cout << time.count() << std::endl;
It comes out to be Monday, January 1, 2024 2:16:48 AM, not 12 AM. I want to represent historic calendar dates. year_month_day
and hh_mm_ss
are available for use, but I enjoy having the ability of getting time_point::time_since_epoch()
. I need this because it is a convenient way of storing date and time data in a database as a single int64. Is there a way to get the best of both worlds?
As I see it my only options are to handle the timings myself and keep using std::chrono
, but this defeats the purpose of using std::chrono
, or to use chrono::year_month_day
and chrono::hh_mm_ss
and deal with the database in a different way.
See this answer for a more in-depth description of chronological computations vs calendrical computations.
But in short, this:
auto time = std::chrono::system_clock::duration::zero();
std::cout << time.count() << std::endl;
time += std::chrono::years(2024 - 1970);
std::cout << time.count() << std::endl;
is a chronological computation. I characterize it as such because it adds a regular unit to a count of regular units. A single std::chrono::years
is nothing more than 31'556'952 seconds
which is the precise length of the average year in the civil calendar (other calendars will have years of different average length).
And in truth, I would not even count the above code as necessarily well posed because it clearly intends that time
be a point in time, and yet it is is declared as a duration
. A better way to perform this chronological computation would be:
using namespace std::literals;
std::chrono::system_clock::time_point time{}; // zero-initialize
std::cout << time << '\n'; // 1970-01-01 00:00:00.000000000
time += 2024y - 1970y;
std::cout << time << '\n'; // 2024-01-01 02:16:48.000000000
Now time
is a time_point
. One can still get the internal count out of it if desired:
std::cout << time.time_since_epoch().count() << '\n';
To perform a calendrical computation one must convert to a calendrical system (there are several), perform the computation in the calendrical system, and then (if desired) convert back to the chronological system.
For example:
std::chrono::system_clock::time_point time{}; // zero-initialize
std::cout << time << '\n'; // 1970-01-01 00:00:00.000000000
std::chrono::year_month_day ymd = std::chrono::floor<std::chrono::days>(time);
time = std::chrono::sys_days{ymd + (2024y - 1970y)};
std::cout << time << '\n'; // 2024-01-01 00:00:00.000000000
As a demonstration of using an alternative calendrical computation consider this:
std::chrono::year_month_weekday ymd = std::chrono::floor<std::chrono::days>(time);
time = std::chrono::sys_days{ymd + (2024y - 1970y)};
std::cout << time << '\n'; // 2024-01-04 00:00:00.000000000
Now instead of adding 54 years to Jan 1, 1970 one is adding 54 years to the first Thursday of Jan 1970 to get the first Thursday of Jan 2024. Indeed year_month_weekday
is truly a full demonstration of an alternate calendar. User written calendars modeling the Julian, Chinese or Islamic calendars could also be used, and likely give different results as well.
I discourage falling back to the C timing API unless you need to do so to interface with legacy code. C++20 and forward has a superset of the date/time functionality of the old C API. It is also much safer and easier to use.
If you identify functionality in the C timing API that you aren't sure how to do in modern <chrono>
, just ask here on stackoverflow and I'm sure someone will be glad to show you how to do it.