3

I want to make a program which takes no input and returns the date of the previous Monday. (I don't care about time zones. And I am only worried about Gregorian calendar). I am using date by Howard Hinnant. This is how I am doing it currently:

#include <iostream>
#include <date/date.h>

int main() {

    auto todays_day = date::year_month_weekday(date::floor<date::days>(std::chrono::system_clock::now()));

    auto todays_date = date::floor<date::days>(std::chrono::system_clock::now());

    int offset = 0;

    auto weekday = todays_day.weekday();

    if(weekday == date::Tuesday)
        offset = 1;
    else if (weekday == date::Wednesday)
        offset = 2;
    else if (weekday == date::Thursday)
        offset = 3;
    else if (weekday == date::Friday)
        offset = 4;
    else if (weekday == date::Saturday)
        offset = 5;
    else if (weekday == date::Sunday)
        offset = 6;

    auto lastMonday = date::year_month_day(todays_date - date::days(offset));

    std::cout << lastMonday;
}

Is there a better way to do this without boost::previous_weekday? (It's not a requirement not to use boost. I am just wondering if it is possible)

Ruslan
  • 18,162
  • 8
  • 67
  • 136
Hemil
  • 916
  • 9
  • 27
  • For a date d represented by an integral type that increases by 1 each day, you can compute the offset by (d + a) % 7 where the constant a depends on your date scheme. – Bathsheba Jun 08 '19 at 09:20
  • Whats 'a' @Bathsheba – Hemil Jun 08 '19 at 09:21
  • It’s the constant (attainable by trial and error) that’s contingent on your date scheme. – Bathsheba Jun 08 '19 at 09:23
  • Is there a wiki page explaining the formula with examples maybe? I have some questions? @Bathsheba – Hemil Jun 08 '19 at 09:26
  • Just play with it. It will be obvious then. – Bathsheba Jun 08 '19 at 09:27
  • Maybe I get it now. Jan 1, 1970 was thursday. The first monday of 1970 was 5th Jan. Therefore if I add 4 to the current date (assuming date is in integral type) and then modulus it with 7, I might get the offset in one statement @Bathsheba – Hemil Jun 08 '19 at 09:36
  • @Hemil yes, and `4` would be the constant `a` that you're looking for. (Assuming that your date scheme starts at `1`, and also that the first day in it is `Jan 1, 1970`, of course) – Not a real meerkat Jun 08 '19 at 13:47

1 Answers1

6

The key to understanding how to do this more simply is knowing this one fact about Howard Hinnant's date library:

weekday difference is circular (or modulo 7 if you prefer). That is, any weekday subtracted from any weekday results in a number of days in the range [0, 6]. This effectively hides the underlying encoding of weekday.

Thus there is no need to translate [Monday, Sunday] into [0, 6] (or any other encoding):

#include "date/date.h"
#include <iostream>

int
main()
{
    auto todays_date = date::floor<date::days>(std::chrono::system_clock::now());
    date::year_month_day lastMonday = todays_date -
                                      (date::weekday{todays_date} - date::Monday);
    std::cout << lastMonday << '\n';
}

Instead you just have to decide how many days you need to subtract from a sys_days (todays_date in this example). That number of days is today's weekday minus Monday. If today is Monday, the result is days{0}. If today is Sunday, the result is days{6}. We could just as well be talking about finding the previous Friday. The logic would not change.

Also, one can directly convert a sys_days to a weekday. No need to go though year_month_weekday.

The code in the OP's question considers the "previous Monday" to be today if today happens to be a Monday. And that is fine. That is what is desired in many "previous weekday" algorithms. And it is the logic I have coded above.

But it is also common to want the previous-weekday-algorithm to result in last week if the weekday you are seeking is today. I.e. if today is Monday, compute a week ago instead of today. That too is easily doable, and by pretty much the same algorithm. One just has to subtract a day at the beginning of the algorithm if you desire this behavior:

auto todays_date = ...
todays_date -= date::days{1};
date::year_month_day lastMonday = ...
Howard Hinnant
  • 206,506
  • 52
  • 449
  • 577
  • Is there a tag for the date library on stack overflow? If not is it logical to add one? I really appreciate your time – Hemil Jun 09 '19 at 07:19
  • I'm not aware of a tag. However [chrono] would not be a bad choice. This library has been voted into the C++20 working draft as part of the `` library. – Howard Hinnant Jun 09 '19 at 12:48