如果给出了特定年份的周数,如何使用 C/C++ 轻松确定一周的第一个和最后一个日期(日和月)?先感谢您!
李
旧问题的新答案。
此答案包括为标准化而提出的代码并被拒绝。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
中的 return 语句week_to_date
可以使用一点解释:
(mon <= jan/_4th/y)
上面的表达式构造了一个日期,它是 y 年 1 月 4 日或之前的星期一。然后,我在该日期之前的每周添加 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
和基于 ISO 周的年份,尽管这些在提案中只是作为如何使用日期类的示例而不是提案的一部分。
随意使用该代码,但我强烈建议先将其从命名空间 std 中删除。源代码足够短,这样的小修改应该不难。
更新
这个答案的链接已经过时,所以我更新了它们。但是,除了更新链接之外,我还想提请注意对上面链接的库的重新设计。重新设计对效率有了新的关注,包括在编译时已知所有输入时的编译时日期计算。
鉴于 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<=
不再可用于查找工作日等于或早于日期的日期。相反,这个功能是由“工作日减法”处理的,它总是产生一个正数(从 weekday1 到 weekday2 需要多少天?)。
该程序的输出与以前的版本相同:
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,有三个通用规则。
避免无休止地修改那种代码并使用库。ICU 图书馆的日历类涵盖了这一点。相关介绍页面在这里。检查页面底部 WEEK_OF_YEAR 字段中的注释。