Given a year, month and day, how do I find the next weekday on or after that date? For example, If I have 2025-04-28, how can I find the Friday following 2025-04-28, which is 2025-05-02?
What about finding the previous weekday instead of the next one?
Can I start with int
values, put them into chrono
and then get int
values back out?
If the input date is already at the target weekday, what is needed to change between getting the input date, or getting a week from the input date?
To answer the first question it is best to break it down into parts:
Let's call the starting date: date
, and the target weekday: wd
.
weekday
of date
. Call that starting_wd
.wd
is ahead of starting_wd
. Call that num_days
.num_days
to date
.This might look like:
std::chrono::year_month_day
next_weekday(std::chrono::year_month_day date, std::chrono::weekday wd)
{
using namespace std::chrono;
weekday starting_wd{date};
days num_days = wd - starting_wd;
return sys_days{date} + num_days;
}
This can be exercised like this:
cout << next_weekday(April/28/2025, Friday) << '\n';
which prints out:
2025-05-02
Notes:
weekday
of date
is already wd
, this adds 0 days
and returns date
.weekdays
is encapsulated away so that you don't
have to care about it. No matter what values wd
and starting_wd
have,
num_days
will always be in the range of days{0}
to days{6}
. You can think
of weekdays
as a "circular range".year_month_day
is correct but inefficient for this operation. Trafficking in
sys_days
would be more efficient.To address this last bullet, we could change both the return type, and the parameter
input type of date
to sys_days
:
std::chrono::sys_days
next_weekday(std::chrono::sys_days date, std::chrono::weekday wd)
{
std::chrono::weekday starting_wd{date};
auto num_days = wd - starting_wd;
return date + num_days;
}
Notes:
year_month_day
is a {y, m, d}
data structure. sys_days
is a
{count of days since epoch}
data structure. Both represent a date. And
conversions from one to the other do not lose information.2025-05-02
.year_month_day
(the type of the expression April/28/2025
) to sys_days
.date
to sys_days
before adding num_days
to it.year_month_day
, those conversions are made implicitly. Thus those
conversions only cost the clients that need them (pay only for what you use
design principle).It is now easy to see that it is trivial to eliminate the local temporaries and make this a one-liner:
std::chrono::sys_days
next_weekday(std::chrono::sys_days date, std::chrono::weekday wd)
{
return date + (wd - std::chrono::weekday{date});
}
What about finding the previous weekday instead of the next one?
In this case you need to subtract the number of days
date
is ahead of wd
:
std::chrono::sys_days
prev_weekday(std::chrono::sys_days date, std::chrono::weekday wd)
{
return date - (std::chrono::weekday{date} - wd);
}
If driven by:
cout << prev_weekday(April/28/2025, Friday) << '\n';
the output is now:
2025-04-25
Can I start with
int
values, put them intochrono
and then getint
values back out?
If you have your year
, month
and day
as integers this looks like:
int iy = 2025;
int im = 4;
int id = 28;
year_month_day next_friday = next_weekday(year{iy}/im/id, Friday);
The return type is sys_days
which is implicitly convertible to
year_month_day
. Use the getters: .year()
, .month()
and .day()
:
auto y = next_friday.year();
auto m = next_friday.month();
auto d = next_friday.day();
In this case y
has type year
, m
has type month
, and d
has type day
.
If you want it as int
, just explicitly cast to int
:
int iy{y}; // iy == 2025
month
and day
have explicit casts to unsigned
for this purpose.
unsigned im{m}; // im == 5
unsigned id{d}; // id == 2
Note: These explicit casts are the exact reverse of going the other way: From integral to chrono types:
year y{iy};
month m{im};
day d{id};
This is a symmetric, easy-to-remember API.
Note: To enable the compiler to detect logic errors in your code, it is best not to convert to integral types, but instead stay inside the chrono type system. This turns logic errors into compile-time errors. But if you have to interoperate with another date library, integers are often the only way to do that.
If the input date is already at the target weekday, what is needed to change between getting the input date, or getting a week from the input date?
This is a very easy transformation: just increment the date
prior to the algorithm
(or decrement in the case of prev_weekday
):
std::chrono::sys_days
next_weekday(std::chrono::sys_days date, std::chrono::weekday wd)
{
++date;
return date + (wd - std::chrono::weekday{date});
}
Note: LLVM has not yet implemented the increment operator on time_point
(as of April 2025).
You can work around this with date += std::chrono::days{1};
instead.
Bonus points: Slap constexpr
onto next_weekday
and it will work at compile-time
too:
static_assert(next_weekday(April/28/2025, Friday) == May/2/2025);