C++ 从C/C+中的周数计算公历日期+;

C++ 从C/C+中的周数计算公历日期+;,c++,c,algorithm,C++,C,Algorithm,我使用的是公历,我想实现IS08601周,但我偶然发现了一个问题:计算任何周数的日期。例如,ISO日期2010-W01-1应返回2010年1月4日,2009-W01-1应返回2008年12月29日 编辑: 我还没有在网上找到任何有效的算法,尝试了很多次,但我现在有点卡住了。也许你应该看看。使用它,您可以编写如下函数: #include <boost/date_time/gregorian/gregorian.hpp> // Get the date for a given year

我使用的是公历,我想实现IS08601周,但我偶然发现了一个问题:计算任何周数的日期。例如,ISO日期
2010-W01-1
应返回2010年1月4日,
2009-W01-1
应返回2008年12月29日

编辑:
我还没有在网上找到任何有效的算法,尝试了很多次,但我现在有点卡住了。

也许你应该看看。使用它,您可以编写如下函数:

#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;
}
编辑:

经过思考,我找到了一种不用boost实现相同功能的方法。代码如下:

警告:下面的代码已损坏,请勿使用

// 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中的代码完全被破坏了,因为我没有花足够的时间来理解周数是如何分配的#


当前接受的答案给出了2017年第1周(以及2017年每周)的错误答案。函数
GetDayAndMonthFromWeekInYear
,分别输入2017年和
year
weekInYear
,应在
month
中输出1和
dayInMonth
,表明2017-W01开始于2017-01-02星期一,但它会输出2017-01-01公历日期

这将使用以下语法输出从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
然后,使用此算法将连续天数转换为
年/月/日
字段类型:

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);
}
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)};
}
CONSTCD14
内联
年·月·日
年\月\日::自\系统\日(常数系统\日和dp)无例外
{
静态断言(标准::数字限制::数字>=18,
“此算法尚未移植到16位无符号整数”);
静态断言(标准::数字限制::数字>=20,
“此算法尚未移植到16位有符号整数”);
自动常数z=dp.time_自_epoch()计数()+719468;
自动常数era=(z>=0?z:z-146096)/146097;
自动常数doe=静态(z-era*146097);/[0146096]
自动常数yoe=(doe-doe/1460+doe/36524-doe/146096)/365;/[0399]
自动常数y=静态施法(yoe)+纪元*400;
自动常数doy=doe-(365*yoe+yoe/4-yoe/100);//[0365]
自动常数mp=(5*doy+2)/153;/[0,11]
自动常数d=doy-(153*mp+2)/5+1;//[1,31]
#ifdef硕士学位
#pragma警告(推送)
#pragma warning(disable:4146)//应用于无符号类型的一元减号运算符,结果仍然无符号
#恩迪夫
自动常数m=mp+(mp<10?3:-9u);//[1,12]
#ifdef\u MSVC\u版本
#布拉格警告(pop)
#恩迪夫

return year\u month\u day{date::year{y+(我还没有找到任何在线算法。您可能会喜欢。答案中有代码。它不是C语言,但它很容易理解,并且不需要花费太多精力进行转换。另外,[
strftime()
])支持格式设置(
%W
%U
%V
,等等)。@pmg我不知道这对我使用公历有什么帮助。你的意思是我应该找到一个使用朱利安日期的算法,然后将结果转换为公历吗?答案是:包含对公历日期类的引用并显示代码(
week_to_date
)从ISO week date转换为gregorian。谢谢,但我们没有使用Boost。当涉及到非Boost示例时,它返回2005年12月18日,即2005年,week为52,weekday为1,此时正确的答案应该是2005年12月26日,请参见。我现在已经修复了自己的算法(见我的答案)但我想你想知道你的代码被破坏了。谢谢,但这只是一个C/C++问题。观看算法标签的帖子
#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
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);
}
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)};
}