4

私はグレゴリオ暦を使用しており、IS0 8601 週を実装したいと考えていますが、週番号の日付を計算する際に問題が発生しました。たとえば、ISO 日付2010-W01-12010 年 1 月 4 日を返し、2008 年 12 月 29 日2009-W01-1を返す必要があります。

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

編集:オンラインで機能するアルゴリズムを見つけられず、たくさん試しましたが、今はちょっと行き詰まっています。

4

3 に答える 3

4

現在受け入れられている回答では、2017 年の第 1 週 (および 2017 年の毎週) に間違った回答が返されます。関数GetDayAndMonthFromWeekInYearは、入力が 2017 および 1 の場合yearweekInYearそれぞれ 1 inmonthおよび 2 inを出力する必要がありますdayInMonth。これは、2017-W01 が 2017-01-02 月曜日に始まることを示しますが、代わりにグレゴリオ暦の日付 2017-01-01 を出力します。

この無料のオープン ソース C++11/14 ライブラリは、次の構文で ISO 週からグレゴリオ暦への正しい日付変換を出力します。

#include "date/date.h"
#include "date/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" および "date.h") を簡単に調べることができます。アルゴリズムも効率的で、反復は使用されません。

一般的なアプローチは、2017_y/1_w/mon次のアルゴリズムを使用して、フィールドを 1970 年 1 月 1 日からの連続した日数に変換することです。

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)};
}

後者のアルゴリズムは、ここで非常に詳細に文書化されています

于 2015-12-06T23:19:39.743 に答える
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;
}

編集:

少し考えた後、ブーストを使用せずに同じ機能を実装する方法を見つけました。コードは次のとおりです。

警告: 以下のコードは壊れています。使用しないでください。

// 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のコードは完全に壊れています。週番号がどのように割り当てられているかを理解するのに十分な時間がなかったためです。

于 2013-03-29T00:03:03.047 に答える
-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)
于 2013-03-29T13:12:20.073 に答える