How*_*ant 3 c++ c++-chrono c++20
我知道我可以使用基于 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;
constexpr iso_week_date(std::chrono::sys_days tp) noexcept;
constexpr operator std::chrono::sys_days() const noexcept;
};
Run Code Online (Sandbox Code Playgroud)
为了演示的目的,我已使其尽可能简单。人们可以很容易地看出,可以为此添加功能,例如年份算术,使数据成员私有并为它们提供 getter 和 setter,添加流和解析运算符等。
采用 a 的构造函数sys_days是问题中要求的转换函数。是operator sys_days逆向转换。这些可以轻松完成noexcept并constexpr启用这些操作的编译时计算。
让我们从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;
}
Run Code Online (Sandbox Code Playgroud)
辅助closest_thursday函数接受 asys_days并将其映射到最近的星期四。也就是说,它在星期一、星期二或星期三加上几天,并从星期五、星期六或星期日减去几天,以产生星期四。这用于获取所tp引用的 ISO 周的正确 ISO 年份,无论一周中的哪一天tp。
计算出正确的 ISO 年份后,ISO 年的开始时间为该年第一个星期四之前的星期一。该表达式(Thursday - Monday)只是一种详细的书写方式days{3},旨在表明该表达式在第一个星期四之前的星期一查找。days{3}如果需要可以替换。这不会影响代码的正确性或性能。
接下来,填写剩余的iso_week_date数据成员就很简单了。请注意,周计数从 1 开始,而不是从 0 开始,因此+ 1.
反向转换(从iso_week_date到sys_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);
}
Run Code Online (Sandbox Code Playgroud)
首先计算 ISO 年的开始时间(第一个星期四之前的星期一)。然后添加正确的周数以及星期一之后的天数。
可以这样练习:
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';
}
Run Code Online (Sandbox Code Playgroud)
这会将 a 转换year_month_day为iso_week_date. 请注意,代码中没有任何地方iso_week_date知道该类型year_month_day。然后它将 a 转换year_weekday_last为iso_week_date. 这恰好与前面的示例是同一日期。并且注意的iso_week_date是不知道类型year_weekday_last。转换是在sys_days引擎盖下进行的。
请注意,如果其他人创建了另一个具有相互转换的日历sys_days(儒略历、希伯来历、玛雅历、伊斯兰历、不同的基于周的日历......),那么也iso_week_date将能够在这些日历之间进行转换。
最后通过逆向转换找出3天后的日期。
输出:
2024-W13 Fri
2024-W13 Fri
2024-04-01
Run Code Online (Sandbox Code Playgroud)
| 归档时间: |
|
| 查看次数: |
183 次 |
| 最近记录: |