如何获取特定年份中指定周数(1-53)的第一个和最后一个日期(日期和月份)?

时间:2012-05-20 09:58:14

标签: c++ date

如果给出特定年份的周数,如何使用C / C ++以简单的方式确定一周的第一个和最后一个日期(日和月)?提前谢谢!

2 个答案:

答案 0 :(得分:2)

旧问题的新答案。

这个答案包括为标准化提出并被拒绝的代码。我提到的原因是,为了提案的目的,代码位于名称空间std::chrono中。但重要的是要意识到此提议未被接受,因此如果您使用它,您应该将建议的部分移动到您自己的命名空间中。它们不属于任何标准,甚至不被考虑用于标准化!

这是user documentation for my date class,这使得这个计算相当容易(假设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中的return语句可以使用一些解释:

(mon <= jan/_4th/y)

上述表达式构造的日期是y年1月4日或之前的星期一。然后我在weeknum之前每周添加7天到该日期:

+ days((weeknum - 1)*7

最后,如果工作日(wd)是星期日,我会添加6天,否则我会添加wd-1天的日期编号为星期一== 1,星期二== 2,截至周六== 6(周日== 0)。

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

最后一步是考虑到基于ISO周的日历周从星期一开始,而这个日期类的实现遵循从星期日开始的一周的C / C ++约定。

proposal实际上包含week_to_date的定义和基于ISO周的年份,尽管这些在提案中仅作为如何使用日期类的示例而不是提案。

随意使用代码,但我强烈建议先从命名空间std中删除它。来源足够短,这样的小修改应该不难。


<强>更新

链接在这个答案上已经过时了,所以我更新了它们。但是,除了更新链接之外,我还想提请注意重新设计与上面链接的库。重新设计重点关注效率,包括编译时所有输入都已知的编译时日期计算。

这是“日期常量”现在是真实的,给予C ++ 14支持。

这个重新设计的库是documented here,其中包含从文档链接的单个标头实现。语法类似,但不完全符合上面的内容:

#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)在运行时输入时效果很好。

节拍继续

将上面介绍的库与新的兼容"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 :(得分:1)

这受到当地编号规则的严重影响。工作日是一周开始的至少有两个常见规则,有一个常见的规则,规定在一年开始时哪一周编号为1。

避免无休止地修补那种代码并使用库。 ICU库用它的Calendar类来覆盖它。相关的介绍页面is here。检查页面底部WEEK_OF_YEAR字段的注释。

相关问题