Как получить первую и последнюю даты (день и месяц) указанного номера недели (1-53) в конкретном году?

Если указан номер недели в определенном году, как я могу легко определить первые и последние даты (день и месяц) недели с помощью C / C ++? Заранее спасибо!

подветренный

Ответы на вопрос(2)

Новый ответ на старый вопрос.

Этот ответ включает код, который был предложен для стандартизации и отклонен. Причина, по которой я упоминаю, состоит в том, что код находится в пространстве именstd::chrono для целей предложения. Но важно понимать, что это предложение не было принято, и поэтому, если вы его используете, вам, вероятно, следует переместить предложенные части в ваше собственное пространство имен. Они не являются частью какого-либо стандарта и даже не рассматриваются для стандартизации!

Вотпользовательская документация для моего класса даты что делает это вычисление довольно простым (при условии стандартной нумерации по ISO). Документация указывает на один заголовок<date> и один источникdate.cpp который реализует предложение.

Используя эти источники и две очень короткие вспомогательные функции, показанные ниже, вот программа, которая берет номер недели и год и распечатывает первые (понедельник) и последние (воскресенье) дни этой недели в гражданском (григорианском) календаре:

#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';
}

Для меня это распечатывает:

2013-02-25
2013-03-03

Заявление о возврате вweek_to_date Можно использовать небольшое объяснение:

(mon <= jan/_4th/y)

Вышеупомянутое выражение создает дату, которая является понедельником 4 января или раньше года y. Затем я добавляю 7 дней для каждой недели доweeknum к этой дате:

+ days((weeknum - 1)*7

И наконец, если день недели (wd) это воскресенье, я добавляю 6 дней, еще я добавляюwd-1 дни, когда дни нумеруются с понедельника == 1, вторника == 2, до субботы == 6 (воскресенье == 0).

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

Этот последний шаг - то, что принимает во внимание, что недельная календарная неделя ISO начинается в понедельник, тогда как эта реализация класса даты следует соглашению C / C ++ недели, начинающейся в воскресенье.

предложение на самом деле включает в себя определениеweek_to_date и недельный год ИСО, хотя они указаны в предложении просто как примеры того, как работать с классом дат, и не являются частью предложения.

Не стесняйтесь использовать код, но я настоятельно рекомендую сначала удалить его из пространства имен std. Источник достаточно короткий, чтобы такие незначительные изменения не были сложными.


Update

Ссылки на этот ответ устарели, и я обновил их. Однако в дополнение к обновлению ссылок я хотел бы обратить внимание на редизайн библиотеки, указанной выше. Редизайн по-новому сфокусирован на эффективности, включая вычисления даты во время компиляции, когда все входные данные известны во время компиляции.

Это "константы даты" теперь реальны, учитывая поддержку C ++ 14.

Эта переработанная библиотеказадокументировано здесь с единственной реализацией заголовка, связанной с документацией. Синтаксис похож, но не точен к тому, что представлен выше:

#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';
}

Два основных различия заключаются в том, что (в C ++ 14) вычисления могут быть сделаныconstexpr, Иoperator<= больше не доступен для поиска даты с рабочим днем, равным или предшествующим дате. Вместо этого эта функция обрабатывается «вычитанием дня недели», которое всегда приводит к положительному числу (сколько дней требуется, чтобы перейти от дня недели1 до дня недели2?).

Эта программа выводит то же, что и предыдущая версия:

2013-02-25
2013-03-03

Большая разница в том, что константы времени компиляции выводятся вstd::cout, Но тот же код (минусstatic_asserts) прекрасно работает с входными данными во время выполнения.

And the beat goes on

Объединение библиотеки, представленной выше, с новым и совместимым"iso_week.h" библиотека, синтаксис для вышеупомянутых вычислений значительно упрощен:

#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';
}

какие выводы:

The dates for 2013-W09 are 2013-02-25 through 2013-03-03

На это сильно влияют местные правила нумерации недель. Существует по крайней мере два общих правила для того, какой день недели является началом недели, есть три общих правила для того, какая неделя имеет номер 1 в начале года.

Избегайте бесконечно возиться с таким кодом и использовать библиотеку. Библиотека ICU покрывает это своими классами Календаря. Соответствующая страница введенияэто здесь, Проверьте заметки в поле WEEK_OF_YEAR внизу страницы.

Ваш ответ на вопрос