C++ Add months to chrono::system_clock::time_point
Overview
This is a very interesting question with several answers. The "correct" answer is something you must decide for your specific application.
With months, you can choose to do either chronological computations or calendrical computations. A chronological computation deals with regular units of time points and time durations, such as hours, minutes and seconds. A calendrical computation deals with irregular calendars that mainly serve to give days memorable names.
The Chronological Computation
If the question is about some physical process months in the future, physics doesn't care that different months have different lengths, and so a chronological computation is sufficient:
The baby is due in 9 months.
What will the weather be like here 6 months from now?
In order to model these things, it may be sufficient to work in terms of the average month. One can create a std::chrono::duration
that has precisely the length of an average Gregorian (civil) month. It is easiest to do this by defining a series of durations starting with days
:
days
is 24 hours:
using days = std::chrono::duration
<int, std::ratio_multiply<std::ratio<24>, std::chrono::hours::period>>;
years
is 365.2425 days
, or 146097/400 days
:
using years = std::chrono::duration
<int, std::ratio_multiply<std::ratio<146097, 400>, days::period>>;
And finally months
is 1/12 of years
:
using months = std::chrono::duration
<int, std::ratio_divide<years::period, std::ratio<12>>>;
Now you can easily compute 8 months from now:
auto t = system_clock::now() + months{8};
Important note: This computation does not preserve the time of day, or even the day of the month.
The Calendrical Computation
It is also possible to add months while preserving time of day and day of month. Such computations are calendrical computations as opposed to chronological computations.
After choosing a calendar (such as the Gregorian (civil) calendar, the Julian calendar, or perhaps the Islamic, Coptic or Ethiopic calendars — they all have months, but they are not all the same months), the process is:
Convert the
system_clock::time_point
to the calendar.Perform the months computation in the calendrical system.
Convert the new calendar time back into
system_clock::time_point
.
You can use Howard Hinnant's free, open-source date/time library to do this for a few calendars. Here is what it looks like for the civil calendar:
#include "date.h"
int
main()
{
using namespace date;
using namespace std::chrono;
// Get the current time
auto now = system_clock::now();
// Get a days-precision chrono::time_point
auto sd = floor<days>(now);
// Record the time of day
auto time_of_day = now - sd;
// Convert to a y/m/d calendar data structure
year_month_day ymd = sd;
// Add the months
ymd += months{8};
// Add some policy for overflowing the day-of-month if desired
if (!ymd.ok())
ymd = ymd.year()/ymd.month()/last;
// Convert back to system_clock::time_point
system_clock::time_point later = sys_days{ymd} + time_of_day;
}
For grins I just ran this, and compared it with now + months{8}
and got:
now is 2017-03-25 15:17:14.467080
later is 2017-11-25 15:17:14.467080 // calendrical computation
now + months{8} is 2017-11-24 03:10:02.467080 // chronological computation
This gives a rough "feel" for how the calendrical computation differs from the chronological computation. The latter is perfectly accurate on average; it just has a deviation from the calendrical on the order of a few days. And sometimes the simpler (latter) solution is close enough, and sometimes it is not. Only you can answer that question.
The Calendrical Computation — Now with timezones
Finally, you might want to perform your calendrical computation in a specific timezone. The previous computation was UTC.
Side note:
system_clock
is not specified to be UTC, but the de facto standard is that it is Unix Time which is a very close approximation to UTC.
You can use Howard Hinnant's free, open-source timezone library to do this computation. This is an extension of the previously mentioned datetime library.
The code is very similar, you just need to convert to local time from UTC, then to a local calendar, do the computation then back to local time, and finally back to system_clock::time_point
(UTC):
#include "tz.h"
int
main()
{
using namespace date;
using namespace std::chrono;
// Get the current local time
auto lt = make_zoned(current_zone(), system_clock::now());
// Get a days-precision chrono::time_point
auto ld = floor<days>(lt.get_local_time());
// Record the local time of day
auto time_of_day = lt.get_local_time() - ld;
// Convert to a y/m/d calendar data structure
year_month_day ymd{ld};
// Add the months
ymd += months{8};
// Add some policy for overflowing the day-of-month if desired
if (!ymd.ok())
ymd = ymd.year()/ymd.month()/last;
// Convert back to local time
lt = local_days{ymd} + time_of_day;
// Convert back to system_clock::time_point
auto later = lt.get_sys_time();
}
Updating our results I get:
now is 2017-03-25 15:17:14.467080
later is 2017-11-25 15:17:14.467080 // calendrical: UTC
later is 2017-11-25 16:17:14.467080 // calendrical: America/New_York
now + months{8} is 2017-11-24 03:10:02.467080 // chronological computation
The time is an hour later (UTC) because I preserved the local time (11:17am) but the computation started in daylight saving time, and ended in standard time, and so the UTC equivalent is later by 1 hour.
I used current_zone()
to pick up my current location, but I could have also used a specific time zone (e.g. "Asia/Tokyo"
).
C++20 Update
As I write this update, technical work has ceased on C++20, and it looks like we will have a new C++ standard later this year (just administrative work left to do to complete C++20).
The advice in this answer translates well to C++20:
For the chronological computation,
std::chrono::months
is supplied by<chrono>
so you don't have to compute it yourself.For the UTC calendrical computation, loose
#include "date.h"
and use instead#include <chrono>
, and dropusing namespace date
, and things will just work.For the time zone sensitive calendrical computation, loose
#include "tz.h"
and use instead#include <chrono>
, dropusing namespace date
, and replacemake_zoned
withzoned_time
, and you're good to go.
How do I add a number of days to a date in C++20 chrono?
A "date" in <chrono>
is just a time point with days-precision. And a
"date-time" is just a chrono::time_point
(usually based onsystem_clock
) with precision finer than days
. And the canonical "date type" in<chrono>
is:
chrono::time_point<chrono::system_clock, chrono::days>
This is nothing but a days
-precision time_point
based onsystem_clock
. This particular time_point
also has a convenience
type alias: sys_days
.
using sys_days = time_point<system_clock, days>;
So to add a number of days to sys_days
one just does:
sys_days tp = ...;
tp += days{n};
// or
auto tp2 = tp + days{n};
If it is just one day, you can also just:
++tp;
This is very efficient because under the hood it is just adding twoint
s.
If your time_point
has precision finer than days
, it is still the
same procedure to add days
to it:
auto tp = system_clock::now() + days{n};
When I try this I get a compile-time error:
auto d = July/4/2020;
auto d2 = d + days{5};
~ ^ ~~~~~~~
error: invalid operands to binary expression
('std::chrono::year_month_day' and 'std::chrono::days')
The variable d
above has type year_month_day
. Even though year_month_day
is semantically equivalent to sys_days
,
it does not directly support days
-precision arithmetic.year_month_day
is a days-precision time point (but not achrono::time_point
). Instead it is a {year, month, day}
data
structure. You can easily perform days-precision arithmetic by
converting it to sys_days
first:
auto d = July/4/2020;
auto d2 = sys_days{d} + days{5};
In this case, the type of the result d2
is sys_days
. If you would like the
result to have year_month_day
, then just convert it back:
year_month_day d2 = sys_days{d} + days{5};
Rationale for this design:
Efficiency. The conversion from year_month_day
to sys_days
(and
back) takes a bit of number crunching. It isn't a huge amount. But
it is large compared to a single integral addition.
If the <chrono>
library provided days
-precision arithmetic for year_month_day
,
the algorithm would be to convert the year_month_day
to sys_days
,
do the arithmetic, and then convert the result back toyear_month_day
. If you have a lot of days
-precision arithmetic to
do, it is better to just traffic in sys_days
, and convert toyear_month_day
only when necessary (i.e. when you need to get theyear
, month
or day
fields out of it).
If the library provided the day-precision arithmetic for year_month_day
,
clients would blindly use it, not realizing that year_month_day
is the
wrong data structure for their application. It would be akin to givingstd::list
an indexing operator (which would be quite easy to do):
template <class T, class A>
T&
list<T, A>::operator[](size_type i)
{
return *advance(begin(), i);
}
Providing such an API just makes it too easy to write inefficient
code. sys_days
is the data structure of choice for doing days
precision arithmetic.
It still doesn't work for me:
auto d = July/4/2020;
auto d2 = sys_days{d} + day{5};
~~~~~~~~~~~ ^ ~~~~~~
error: invalid operands to binary expression
('std::chrono::sys_days' and 'std::chrono::day')
You need to use days
, not day
. day
is a calendrical specifier
for the day of a month in the civil calendar. days
is achrono::duration
. See this stack overflow Q/A for a more in-depth
discussion about the distinction between these two concepts.
If I have a
year_month_day
and I want to add a few days to it that
I know won't reach the end of the month, can I do that without
converting tosys_days
and thus gain efficiency?
Yes.
auto d = July/4/2020;
auto d2 = d.year()/d.month()/(d.day() + days{5});
The above simply adds 5 to the day
field of d
. There is no
checking to see if the result goes past the last day of the month. If
it does go past the last day of the month, that result will be stored
in the day field (up to day 255), and d2.ok()
will return false
.
year_month_day
is good for retrieving the year
, month
or day
fields from a date. It is also good for years
and months
precision calendrical arithmetic. sys_days
is good for days
precision arithmetic, and for converting to a finer precision
(a date-time).
year_month_day
and sys_days
can convert to one another with no
information loss. Use whichever data structure makes the most sense.
And if you forget, the compiler will remind you, just like it does forvector
(no push_front
), list
(no indexing operator), andforward_list
(no size
).
Add time duration to C++ timepoint
If you want to add five hours to startTimePoint
, it's boringly simple:
startTimePoint += hours(5); // from the alias std::chrono::hours
Live example.
By the way, you're trying to convert a steady_clock::now()
into a system_clock::time_point
, which shouldn't even compile. Change the steady_clock::now()
to system_clock::now()
and you should be good to go.
How to add chrono::year_month_day to chrono::sys_seconds
This is chrono catching logic bugs for you at compile-time. Adding date2 + calDuration
is akin to adding tomorrow + today. It just doesn't make sense. And that's why it is a compile-time error.
What you may mean is that you have durations years
, months
and days
. This is not the same as the similarly named types year
, month
and day
. The plural forms are chrono::duration
s, just like minutes
and nanoseconds
. days
is 24h
. And years
and months
are the average length of those units in the civil calendar.
Conversely, the singular forms year
, month
and day
are the calendrical components that give a name to a day in the civil calendar, e.g. 2020y
, December
, and 18d
. And it is these singular forms that make up a year_month_day
, e.g. 2020y/December/18d
.
See this SO answer for a deep-dive on the difference between month
and months
.
There are multiple ways to add the units years
and months
to a time_point
. See this SO answer for a deep-dive on that topic.
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
).
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();
std::chrono: add custom duration to time_point
The conversion fails, because there is no conversion from
std::chrono::time_point< std::chrono::system_clock,
std::chrono::system_clock::duration >
to
std::chrono::time_point< std::chrono::system_clock,
std::chrono::duration< double > >
The easiest way would be to give double_prec_seconds
as a template parameter to time_point
, see std::chrono::time_point
typedef std::chrono::time_point< std::chrono::system_clock,
double_prec_seconds > timepoint_t;
then you already have the proper type for t3
and do_something
.
Add time of day information to year_month_day with Howard Hinnant's date library
using namespace std::chrono;
auto tp = date::sys_days{X} + hours{A} + minutes{B} + seconds{C};
The type of tp
is std::chrono::time_point<system_clock, seconds>
, and represents a time point in UTC.
The only thing Howard Hinnant's date library adds in this example is the conversion from date::year_month_day
to sys_days
, which itself is just a typedef
for time_point<system_clock, days>
. After that conversion, you're working entirely within the C++11/14 <chrono>
library.
Trying to subtract a std::chrono::duration from a time_point (C++), but my code isn't compiling. Any suggestions?
float numSeconds = 50;
The following line:
std::chrono::steady_clock::time_point startTime = std::chrono::steady_clock::now();
could be more concisely written as:
auto startTime = std::chrono::steady_clock::now();
Both syntaxes are equivalent and correct.
The reason this doesn't compile:
auto duration = std::chrono::duration<float, std::chrono::seconds>(numSeconds);
is that the second template argument to duration
takes a std::ratio
, not a std::chrono::duration
. The correct way to say this is:
auto duration = std::chrono::duration<float, std::ratio<1, 1>>(numSeconds);
The above means that duration
has type std::chrono::duration
with a representation of float
and a period of 1/1 seconds. That means that this is just a floating point count of seconds.
The second template parameter of std::ratio
defaults to 1, so the above can be simplified to:
auto duration = std::chrono::duration<float, std::ratio<1>>(numSeconds);
And the second template parameter of std::chrono::duration
defaults to std::ratio<1>
so the above can be further simplified to:
auto duration = std::chrono::duration<float>(numSeconds);
The following doesn't compile:
startTime -= duration;
because chrono
has a rule that says values with floating-point representation never implicitly convert to those with integral representation. This is to avoid the truncating error that results when one assigns a float
to an int
.
There are several ways to fix this. For example you could store the result in a float
-based time_point
:
auto anotherTime = startTime - duration;
anotherTime
has type std::chrono::time_point<std::chrono::steady_clock, std::chrono::duration<float, std::chrono::steady_clock::period>>
.
Or you could explicitly cast duration
back to integral type:
startTime -= std::chrono::duration_cast<std::chrono::seconds>(duration);
If you choose the latter, then there really is no point in using float
at all. And the entire sequence could look like:
using namespace std::chrono_literals;
auto startTime = std::chrono::steady_clock::now();
auto duration = 50s;
startTime -= duration;
or:
using namespace std::chrono_literals;
auto startTime = std::chrono::steady_clock::now();
startTime -= 50s;
or:
using namespace std::chrono_literals;
auto startTime = std::chrono::steady_clock::now() - 50s;
Here is a 1h video tutorial on <chrono>
that you may find helpful: https://www.youtube.com/watch?v=P32hvk8b13M
Related Topics
Paint a Rect on Qglwidget at Specifit Times
Undefined Reference to 'Stdscr' While Using Ncurses
How Performing Multiple Matrix Multiplications in Cuda
How to Avoid Including Class Implementation Files
What Is C++ Version of Realloc(), to Allocate the New Buffer and Copy the Contents from the Old One
What Does "#Define Str(A) #A" Do
Stopping the Debugger When a Nan Floating Point Number Is Produced
How to Show Enter Password in the Form of Asterisks(*) on Terminal
Are Class Members Guaranteed to Be Contiguous in Memory
How to Use Dynamic Name for Variables in C++
What Happens When You Bit Shift Beyond the End of a Variable
Comparing Character Arrays and String Literals in C++
Why I Can Access Member Functions Even After the Object Was Deleted
Operator Overload Which Permits Capturing with Rvalue But Not Assigning To