2013-03-28 15 views
4

Я использую григорианский календарь, и я хочу реализовать IS0 8601 недель, но я наткнулся на вопрос, вычисляющий дату любого номера недели. Например, даты ISO 2010-W01-1 должны возвращаться 4 января 2010 г. и 2009-W01-1 должны вернуть 29 декабря 2008 г..Вычислить григорианскую дату с номера недели в C/C++

// Get the date for a given year, week and weekday(1-7) 
time_t *GetDateFromWeekNumber(int year, int week, int dayOfWeek) 
{ 
    // Algorithm here 
} 

Edit: Я гавань нашел любой алгоритм, который работает в Интернете, пробовал много, но я вроде застрял в настоящее время.

+0

Я не нашел ни одного алгоритма, который работает в Интернете. – 2013-03-28 20:33:51

+0

Вам может понравиться [статья о юлианских датах в Википедии] (http://en.wikipedia.org/wiki/Julian_date). – pmg

+0

В ответ на [Как рассчитать номер недели с датой?] (Http://stackoverflow.com/questions/274861/how-do-i-calculate-the-week-number-given-a -date/275024 # 275024). Это не в C, но он довольно легко понятен и не требует больших усилий для преобразования. Кроме того, ['strftime()'] (http://pubs.opengroup.org/onlinepubs/9699919799/functions/strftime.html) поддерживает форматирование ('% W','% U', '% V' и т. Д.). –

ответ

3

Принятая в настоящее время ответ дает неправильный ответ на 1-й неделе 2017 года (и каждую неделю 2017 года). Функция GetDayAndMonthFromWeekInYear с учетом ввода 2017 и 1 для year10 и weekInYear соответственно должна выводить 1 в month и 2 в dayInMonth, указывая, что 2017-W01 начинается в Mon, 2017-01-02, но вместо этого выводит григорианскую дату 2017- 01-01.

Этот free, open source, C++11/14 library выводит правильное преобразование даты из ISO недели Григорианский с этим синтаксисом:

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

int 
main() 
{ 
    using namespace iso_week::literals; 
    std::cout << date::year_month_day{2017_y/1_w/mon} << '\n'; 
} 

2017-01-02 

Как библиотека с открытым исходным кодом, можно легко проверить источники («iso_week.h» и «даты. h ") для используемых алгоритмов. Алгоритмы также эффективны, без использования итерации.

Общий подход заключается в преобразовании поля 2017_y/1_w/mon в последовательный подсчет дней с 1970-01-01, используя этот алгоритм:

CONSTCD14 
inline 
year_weeknum_weekday::operator sys_days() const NOEXCEPT 
{ 
    return sys_days{date::year{int{y_}-1}/date::dec/date::thu[date::last]} 
     + (date::mon - date::thu) + weeks{unsigned{wn_}-1} + (wd_ - mon); 
} 

А потом что серийный отсчет дней преобразуется в year/month/day поле типа, используя этот алгоритм:

CONSTCD14 
inline 
year_month_day 
year_month_day::from_sys_days(const sys_days& dp) NOEXCEPT 
{ 
    static_assert(std::numeric_limits<unsigned>::digits >= 18, 
      "This algorithm has not been ported to a 16 bit unsigned integer"); 
    static_assert(std::numeric_limits<int>::digits >= 20, 
      "This algorithm has not been ported to a 16 bit signed integer"); 
    auto const z = dp.time_since_epoch().count() + 719468; 
    auto const era = (z >= 0 ? z : z - 146096)/146097; 
    auto const doe = static_cast<unsigned>(z - era * 146097);   // [0, 146096] 
    auto const yoe = (doe - doe/1460 + doe/36524 - doe/146096)/365; // [0, 399] 
    auto const y = static_cast<sys_days::rep>(yoe) + era * 400; 
    auto const doy = doe - (365*yoe + yoe/4 - yoe/100);    // [0, 365] 
    auto const mp = (5*doy + 2)/153;         // [0, 11] 
    auto const d = doy - (153*mp+2)/5 + 1;        // [1, 31] 
#ifdef _MSC_VER 
#pragma warning(push) 
#pragma warning(disable: 4146) // unary minus operator applied to unsigned type, result still unsigned 
#endif 
    auto const m = mp + (mp < 10 ? 3 : -9u);       // [1, 12] 
#ifdef _MSVC_VER 
#pragma warning(pop) 
#endif 
    return year_month_day{date::year{y + (m <= 2)}, date::month(m), date::day(d)}; 
} 

последний алгоритм documented in excruciating detail here.

+0

Спасибо, я удалил свой ответ, чтобы его не использовали. – 2015-12-06 23:37:37

1

Возможно, вам стоит посмотреть на boost::date_time::gregorian. С его помощью вы можете написать функцию так:

#include <boost/date_time/gregorian/gregorian.hpp> 

// Get the date for a given year, week and weekday(0-6) 
time_t *GetDateFromWeekNumber(int year, int week, int dayOfWeek) 
{ 
    using namespace boost::gregorian; 
    date d(year, Jan, 1); 
    int curWeekDay = d.day_of_week(); 
    d += date_duration((week - 1) * 7) + date_duration(dayOfWeek - curWeekDay); 
    tm tmp = to_tm(d); 
    time_t * ret = new time_t(mktime(&tmp)); 
    return ret; 
} 

К сожалению, их формат даты отличается от вашего - они нумеруют дни недели, начиная с воскресенья, то есть Sunday = 0, Monday = 1, ..., Saturday = 6. Если он не удовлетворяет вашим потребностям, вы можете использовать эту слегка измененную функцию:

#include <boost/date_time/gregorian/gregorian.hpp> 

// Get the date for a given year, week and weekday(1-7) 
time_t *GetDateFromWeekNumber(int year, int week, int dayOfWeek) 
{ 
    using namespace boost::gregorian; 
    date d(year, Jan, 1); 
    if(dayOfWeek == 7) { 
     dayOfWeek = 0; 
     week++; 
    } 
    int curWeekDay = d.day_of_week(); 
    d += date_duration((week - 1) * 7) + date_duration(dayOfWeek - curWeekDay); 
    tm tmp = to_tm(d); 
    time_t * ret = new time_t(mktime(&tmp)); 
    return ret; 
} 

EDIT:

Подумав немного, я нашел способ реализовать ту же функцию, не используя импульс. Вот код:

ВНИМАНИЕ: приведенный ниже код не работает, не используйте его!

// Get the date for a given year, week and weekday(1-7) 
time_t *GetDateFromWeekNumber(int year, int week, int dayOfWeek) 
{ 
    const time_t SEC_PER_DAY = 60*60*24; 
    if(week_day == 7) { 
     week_day = 0; 
     week++; 
    } 
    struct tm timeinfo; 
    memset(&timeinfo, 0, sizeof(tm)); 
    timeinfo.tm_year = year - 1900; 
    timeinfo.tm_mon = 0; 
    timeinfo.tm_mday = 1; 
    time_t * ret = new time_t(mktime(&timeinfo)); // set all the other fields 
    int cur_week_day = timeinfo.tm_wday; 
    *ret += sec_per_day * ((week_day - cur_week_day) + (week - 1) * 7); 
    return ret; 
} 

EDIT2:

Да, код в EDIT полностью сломана, потому что я не принимал достаточно времени, чтобы понять, как номера недели назначены.

+0

Спасибо, но мы не используем Boost. – 2013-03-29 14:47:23

+1

Когда дело доходит до вашего примера, отличного от Boost, тогда он возвращает _18 декабря 2005 года_, когда год составляет 2005, неделя равна 52, а рабочий день - 1, когда правильный ответ должен был быть 26 декабря 2005 года_, см. Http: // www. Эпоха. Теперь я исправил свой собственный алгоритм (см. Мой ответ), но я думал, что вы хотели бы знать, что ваш код сломан. – 2013-03-29 15:19:55

-1

К F #

open System 
open System.Globalization 

//wday: 1-7, 1:Monday 
let DateFromWeekOfYear y w wday = 
    let dt = new DateTime(y, 1, 4) //first week include 1/4 
    let dow = if dt.DayOfWeek = DayOfWeek.Sunday then 7 else int dt.DayOfWeek //to 1-7 
    let dtf = dt.AddDays(float(wday - dow)) 
    GregorianCalendar().AddWeeks(dtf, w - 1) 
+0

Спасибо, но это только вопрос C/C++. – 2013-03-29 14:56:38

+0

Сообщение тега алгоритма просмотра – BLUEPIXY

Смежные вопросы