我知道我可以通过基于ISO周的日历(
%G
,%V
和%u
)格式化计时日期,但是我如何使用<chrono>
?来计算这些值
我建议创建一种类型,该类型持有基于ISO周的年度,周数和工作日,并提供该类型的转换功能往返
std::chrono::sys_days
。 这为您提供了所需的内容(转换为基于ISO周的日历)和基于ISO周的日历。 它使您与任何其他具有往返于sys_days
的转换的日历系统互操作。
此数据结构的初稿可能看起来像:
struct iso_week_date
{
std::int16_t year;
std::uint8_t weeknum;
std::chrono::weekday dow;
constexpr iso_week_date(int y, unsigned wn, std::chrono::weekday wd) noexcept : year{ y }, weeknum{ wn }, dow{ wd } {}
constexpr iso_week_date(std::chrono::sys_days tp) noexcept;
constexpr operator std::chrono::sys_days() const noexcept;
};
为了示范目的,我尽可能简单。 人们可以轻松地看到一个人可以在此添加功能,例如年度算术,使数据成员私密,并给予他们获取器和固定器,添加流和解析操作员等。
采用A的构造函数是问题中要求的转换函数。 sys_days
operator sys_days
noexcept
constexpr
启用编译时间计算。 llet从
sys_days
构造器开始:
关于基于ISO周的日历,首先要了解的是,它将一年分为52或53周,周一开始。 每个星期都在一年的ISO年内完全。 因此,ISO年并不总是与民事年份相同,尽管通常是。 在民事年份的开始或结束时,ISO年可能是公民年份的一个。 例如,当12月31日落在星期一时,该星期一的股票与下周四的同年相同,比周一大的年度大。对于星期四以外的每一天,民事年份可能比ISO年前落后一个或一个。 但是在星期四,民事年度和ISO年总是相同的。 结合一个星期(星期一至周日)始终在同一年中,这对于计算从
sys_days
到iso_week_date
的转换很重要。
constexpr
iso_week_date::iso_week_date(std::chrono::sys_days tp) noexcept
: iso_week_date{0, 0, std::chrono::weekday{}}
{
using std::chrono::sys_days;
using std::chrono::year_month_day;
using std::chrono::Monday;
using std::chrono::Thursday;
using std::chrono::weeks;
using std::chrono::weekday;
using std::chrono::days;
using std::chrono::floor;
dow = weekday{tp};
auto closest_thursday = [this](sys_days tp)
{
auto i = static_cast<int>(dow.iso_encoding());
tp += days{4-i};
return tp;
};
auto y = year_month_day{closest_thursday(tp)}.year();
auto start = sys_days{y/1/Thursday[1]} - (Thursday - Monday);
year = int{y};
weeknum = floor<weeks>(tp - start) / weeks{1} + 1;
}
辅助功能将其绘制为closest_thursday
sys_days
都指的是。 一旦计算正确的ISO年,ISO年的开始是一年中第一个星期四之前的星期一。 这一表达只是一种详细的写作方式,旨在表明该表达在第一个星期四之前的星期一找到。 如果需要,请替换。 这不会影响代码的正确性或性能。填写剩余的
tp
数据成员是直接的。 请注意,一周的数量从1开始,而不是0,因此tp
。反向转换(从
(Thursday - Monday)
到
days{3}
)甚至更容易:
days{3}
ISO年开始计算(第一个星期四之前的星期一)。然后添加正确的几周数和星期一之后的天数。
this可以这样行使这一点:iso_week_date
这将A
+ 1
转换为iso_week_date
。 请注意,代码中的任何地方都没有意识到类型。 然后将Asys_days
转换为constexpr
iso_week_date::operator std::chrono::sys_days() const noexcept
{
using std::chrono::sys_days;
using std::chrono::Monday;
using std::chrono::Thursday;
using std::chrono::weeks;
auto start = sys_days{std::chrono::year{year}/1/Thursday[1]} - (Thursday - Monday);
return start + weeks{weeknum-1} + (dow - Monday);
}
。 这恰好与上一个示例相同。 并请注意,
int
main()
{
using namespace std::chrono;
iso_week_date d1{2024y/March/29};
std::cout << d1.year << "-W" << unsigned{d1.weeknum} << " " << d1.dow << '\n';
iso_week_date d2{Friday[last]/March/2024};
std::cout << d2.year << "-W" << unsigned{d2.weeknum} << " " << d2.dow << '\n';
sys_days d3 = sys_days{d2} + days{3};
std::cout << d3 << '\n';
}
不知道类型year_month_day
。 转换在引擎盖下弹跳。
注意,如果其他人创建了另一个具有往返于iso_week_date
(朱利安,希伯来语,玛雅,伊斯兰,基于周的日历的日历...)的日历,也将也能够转换为这些日历。最终使用反向转换来查找3天后的日期。
输出:
iso_week_date
demo.