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
intvalues, put them intochronoand then getintvalues 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);
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With