How to get the first and the last dates (day and month) of a specified week number (1-53) in a particular year?

后端 未结 2 653
野趣味
野趣味 2021-01-23 06:08

If the week number in a particular year is given, how can I determine the the first and the last dates (day and month) of the week in an easy way by using C/C++? Thank you in ad

相关标签:
2条回答
  • 2021-01-23 06:19

    New answer for an old question.

    This answer includes code that was proposed for standardization and turned down. The reason I mention that is that the code is in namespace std::chrono for the purposes of the proposal. But it is important to realize that this proposal was not accepted and thus if you use it, you should probably move the proposed parts into your own namespace. They are not part of any standard, and not even being considered for standardization!

    Here is user documentation for my date class which makes this computation fairly easy (assuming ISO standard week numbering). The documentation points to one header <date> and one source date.cpp which implements the proposal.

    Using these sources, and two very brief helper functions shown below, here is a program that takes a week number and year, and prints out the first (Monday) and last (Sunday) days of that week in the civil (Gregorian) calendar:

    #include "date"
    #include <utility>
    #include <iostream>
    
    std::chrono::date
    week_to_date(int weeknum, std::chrono::weekday wd, std::chrono::year y)
    {
        using namespace std::chrono;
        return (mon <= jan/_4th/y) + days((weeknum - 1)*7 + (wd == 0 ? 6 : wd - 1));
    }
    
    std::pair<std::chrono::date, std::chrono::date>
    get_dates(unsigned week_num, unsigned y)
    {
        using namespace std::chrono;
        return std::make_pair(week_to_date(week_num, mon, year(y)),
                              week_to_date(week_num, sun, year(y)));
    }
    
    int main()
    {
        auto p = get_dates(9, 2013);
        std::cout << p.first << '\n';
        std::cout << p.second << '\n';
    }
    

    For me this prints out:

    2013-02-25
    2013-03-03
    

    The return statement in week_to_date could use a little explanation:

    (mon <= jan/_4th/y)
    

    The above expression constructs a date which is the Monday on or before Jan 4 of the year y. I then add 7 days for each week prior to weeknum to that date:

    + days((weeknum - 1)*7
    

    And finally if the week day (wd) is a Sunday, I add 6 days, else I add wd-1 days where the days are numbered with Monday == 1, Tuesday == 2, up to Saturday == 6 (Sunday == 0).

    + (wd == 0 ? 6 : wd - 1))
    

    This last step is what takes into account that the ISO week-based calendar week starts on Monday, whereas this implementation of a date class follows the C/C++ convention of the week starting on Sunday.

    The proposal actually includes the definition of week_to_date and the ISO week-based year, though these are in the proposal just as examples of how to work with the date class and are not part of the proposal.

    Feel free to use the code, but I strongly recommend removing it from namespace std first. The source is short enough that minor modifications like this should not be difficult.


    Update

    The links had gone stale on this answer and so I've updated them. However in addition to updating the links I would like to draw attention to a redesign of the library linked to above. The redesign has a new focus on efficiency, including compile-time date computations when all of the inputs are known at compile time.

    That is "date constants" are now real, given C++14 support.

    This redesigned library is documented here with a single header implementation linked from the documentation. The syntax is similar, but not exact to what is presented above:

    #include "date.h"
    #include <iostream>
    
    constexpr
    date::year_month_day
    week_to_date(int weeknum, date::weekday wd, date::year y)
    {
        using namespace date;
        auto const dp = sys_days(jan/4/y);
        auto const wdjan4 = weekday(dp);
        return dp - (wdjan4 - mon)
           + days((weeknum - 1)*7 + (wd == sun ? 6 : static_cast<unsigned>(wd) - 1));
    }
    
    constexpr
    std::pair<date::year_month_day, date::year_month_day>
    get_dates(int week_num, date::year y)
    {
        using namespace date;
        return std::make_pair(week_to_date(week_num, mon, y),
                              week_to_date(week_num, sun, y));
    }
    
    int
    main()
    {
        using namespace date;
        constexpr auto p = get_dates(9, 2013_y);
        static_assert(p.first == feb/25/2013, "");
        static_assert(p.second == 2013_y/mar/3, "");
        std::cout << p.first << '\n';
        std::cout << p.second << '\n';
    }
    

    The two major differences are that (in C++14) the computations can be made constexpr. And the operator<= is no longer available for finding a date with a weekday equal to or before a date. Instead this functionality is handled by "weekday subtraction", which always results in a positive number (how many days does it take to get from weekday1 to weekday2?).

    This program outputs the same as the previous version did:

    2013-02-25
    2013-03-03
    

    The big difference now is that compile-time constants are being output to std::cout. But the same code (minus the static_asserts) works just fine with run-time inputs.

    And the beat goes on

    Combining the library introduced above, with a new and compatible "iso_week.h" library, the syntax for the above computation is greatly simplified:

    #include "date.h"
    #include "iso_week.h"
    #include <iostream>
    
    int
    main()
    {
        using namespace iso_week::literals;
        auto y = 2013_y;
        auto wn = 9_w;
        std::cout << "The dates for " << y/wn << " are "
                  << date::year_month_day{y/wn/mon} << " through "
                  << date::year_month_day{y/wn/sun} << '\n';
    }
    

    which outputs:

    The dates for 2013-W09 are 2013-02-25 through 2013-03-03
    
    0 讨论(0)
  • 2021-01-23 06:24

    This is heavily affected by local rules for week numbering. There are at least two common rules for what week day is the start of a week, there are three common rules for what week is numbered 1 at the start of the year.

    Avoid having to endlessly tinker with that kind of code and use a library. The ICU library covers this with its Calendar classes. The relevant introduction page is here. Check the notes on the WEEK_OF_YEAR field at the bottom of the page.

    0 讨论(0)
提交回复
热议问题