C++ chrono日历功能的用法(非常详细)
C++11 提供的 chrono 库提供了对时钟、时间点和持续时间的支持,但表示时间和日期并不容易,特别是在日历和时区方面。
C++20 标准通过扩展现有的 chrono 库纠正了这一点:
在本节中,我们将学习如何使用日历对象。所有 chrono 功能都在
1) 用年、月、日表示公历日期,作为 year_month_day 类型的实例。使用标准的用户自定义字面量、常量和重载操作符/来构造这样的对象:
2) 将特定年月日的第 n 个工作日表示为 year_month_weekday 类型的实例:
3) 确定当前日期,并基于它计算其他日期,例如明天和昨天对应的日期:
4) 确定特定年份和月份的第一天和最后一天:
5) 计算两个日期之间的天数:
6) 检查日期是否有效:
7) 使用 time_of_day<Duration> 类模板以时、分和秒形式表示一天中的时间:
8) 创建带有日期和时间部分的时间点:
9) 确定一天中的当前时间,并以不同的精度表示:
下表列出了 std::chrono 命名空间中的所有类型及其所代表的含义:
表中列出的所有类型有:
此外,operator/ 针对许多类型进行了重载,以使我们能够轻松地创建公历日期。当创建日期(年、月、日)时,可以选择三种不同的格式:
在这些情况下,“日”可以是:
为了消除表示日、月和年的整数间的歧义,标准库提供了两个用户自定义字面量:""y 用于构造 std::chrono::year 类型的字面量,""d 用于构造 std::chrono::day 类型的字面量。
此外,还有一些常量表示:
我们可以使用它们来构造日期,例如 2020y/April/1、25d/December/2020 或 Sunday[last]/May/2020。
year_month_day 类型提供了与 std::chrono::sys_days 之间的隐式转换,该类型是一个 std::chrono::time_point,精度为一天(24 小时)。还有一个称为 std::chrono::sys_seconds 的伴生类型,它是一个精确到 1 秒的 time_point。
time_point 和 sys_days/sys_seconds 之间的显式转换,可以使用 std::chrono::time_point_cast() 或 std::floor() 来执行。
要表示一天中的某个时刻,可以使用 std::chrono::time_of_day 类型,该类型表示从午夜开始经过的时间,以时、分、秒和子秒的形式表示。这个类模板针对不同的精度(std::chrono::hours、std::chrono::minutes 和 std::chrono::seconds)进行了特化。此类型主要用作格式化工具,它有两个名为 make12() 和 make24() 的成员,它们用于将输出的时间格式更改为 12 小时或 24 小时格式。
自 C++20 以来,这个时钟测量 Unix 时间,即自 1970 年 1 月 1 日 00:00:00 UTC 开始的时间。这意味着隐式时区是 UTC。但是,在大多数情况下,人们感兴趣的可能是特定时区的当地时间。为此,chrono 库还增加了对时区的支持。
C++20 标准通过扩展现有的 chrono 库纠正了这一点:
- 支持更多的时钟,如 UTC 时钟、国际原子时间时钟、GPS 时钟、文件时间时钟和代表本地时间的伪时钟;
- 支持一天内的时间,表示从午夜开始经过的时间,分为时、分和秒三种;
- 支持日历,这使我们能够用年、月和日部分来表达日期;
- 支持时区,这使我们能够相对于时区表示时间点,使在不同时区之间转换时间成为可能;
- 通过 I/O 支持从流中解析 chrono 对象。
在本节中,我们将学习如何使用日历对象。所有 chrono 功能都在
<chrono>
头文件中 std::chrono 和 std::chrono_literals 命名空间中可用。C++ chrono使用方式
我们可以使用 C++20 的 chrono 日历功能来:1) 用年、月、日表示公历日期,作为 year_month_day 类型的实例。使用标准的用户自定义字面量、常量和重载操作符/来构造这样的对象:
// format: year / month / day year_month_day d1 = 2020y / 1 / 15; year_month_day d2 = 2020y / January / 15; // format: day / month / year year_month_day d3 = 15d / 1 / 2020; year_month_day d4 = 15d / January / 2020; // format: month / day / year year_month_day d5 = 1 / 15d / 2020; year_month_day d6 = January / 15 / 2020;
2) 将特定年月日的第 n 个工作日表示为 year_month_weekday 类型的实例:
// format: year / month / weekday year_month_weekday d1 = 2020y / January / Monday[1]; // format: weekday / month / year year_month_weekday d2 = Monday[1] / January / 2020; // format: month / weekday / year year_month_weekday d3 = January / Monday[1] / 2020;
3) 确定当前日期,并基于它计算其他日期,例如明天和昨天对应的日期:
auto today = floor<days>(std::chrono::system_clock::now()); auto tomorrow = today + days{ 1 }; auto yesterday = today - days{ 1 };
4) 确定特定年份和月份的第一天和最后一天:
year_month_day today = floor<days>(std::chrono::system_clock::now()); year_month_day first_day_this_month = today.year() / today.month() / 1; year_month_day last_day_this_month = today.year() / today.month() / last; year_month_day last_day_feb_2020 = 2020y / February / last; year_month_day last_ymd{today.year(),month_day_last{month{ 2 } }}; year_month_day last_ymd{ };
5) 计算两个日期之间的天数:
inline int number_of_days(date::sys_days const& first, date::sys_days const& last) { return (last - first).count(); } auto days = number_of_days(2020_y / apr / 1, 2020_y / dec / 25);
6) 检查日期是否有效:
auto day = 2020_y / January / 33; auto is_valid = day.ok();
7) 使用 time_of_day<Duration> 类模板以时、分和秒形式表示一天中的时间:
time_of_day<std::chrono::seconds> td{13h + 12min + 11s}; std::cout << td << '\n'; // 13:12:11
8) 创建带有日期和时间部分的时间点:
auto tp = sys_days{ 2020_y / April / 1 } + 12h + 30min + 45s; std::cout << tp << '\n'; // 2020-04-01 12:30:45
9) 确定一天中的当前时间,并以不同的精度表示:
auto tp = std::chrono::system_clock::now(); auto dp = floor<days>(tp); time_of_day<std::chrono::milliseconds> time{ std::chrono::duration_cast<std::chrono::milliseconds>(tp - dp) }; std::cout << time << '\n'; // 13:12:11.625 time_of_day<std::chrono::minutes> time{std::chrono::duration_cast<std::chrono::minutes>(tp - dp) }; std::cout << time << '\n'; // 13:12
C++ chrono工作原理
我们在示例中看到的 year_month_day 和 year_month_weekday 类型只是为支持日历而添加到 chrono 库的许多新类型中的一部分。下表列出了 std::chrono 命名空间中的所有类型及其所代表的含义:
类型 | 描述 |
---|---|
day | 一月中的一天 |
month | 一年中的一月 |
year | 公历中的一年 |
weekday | 公历一周中的一天 |
weekday_indexed | 一月中的第 n 个工作日,其中n的取值范围为 [1,5](1 代表一月中的第一个工作日,5 为第 5 个) |
weekday_last | 一月中的最后一个工作日 |
month_day | 特定月份的特定天 |
month_day_last | 特定月份的最后一天 |
month_weekday | 特定月份的第n个工作日 |
month_weekday_last | 特定月份的最后一个工作日 |
year_month | 特定年份的特定月份 |
year_month_day | 特定的年月日 |
year_month_day_last | 特定年份和月份的最后一天 |
year_month_weekday | 特定年份和月份的第 n 个工作日 |
year_month_weekday_last | 特定年份和月份的最后一个工作日 |
表中列出的所有类型有:
- 不初始化成员字段的默认构造函数;
- 访问实体各部分的成员函数;
- 一个名为 ok() 的成员函数(用于检查存储的值是否有效);
- 用于比较类型值的非成员比较操作符;
- 重载 operator<<(将该类型的值输出到流);
- 一个名为 from_stream() 的重载函数模板(根据提供的格式解析流中的值);
- 文本格式化库的 std::formatter<T,CharT> 类模板的特化。
此外,operator/ 针对许多类型进行了重载,以使我们能够轻松地创建公历日期。当创建日期(年、月、日)时,可以选择三种不同的格式:
- 年/月/日(适用于中国、日本、韩国、加拿大等国家,也可用于其他国家,有时与日/月/年格式结合使用);
- 月/日/年(美国使用);
- 日/月/年(在世界大部分地区使用)。
在这些情况下,“日”可以是:
- 一个月中实际的一天(取值范围从 1 到 31);
- std::chrono::last,表示每月的最后一天;
- weekday[n],表示每月的第 n 个工作日(其中 n 可以取 1 到 5 的值);
- weekday[std::chrono::last],表示一个月的最后一周;
为了消除表示日、月和年的整数间的歧义,标准库提供了两个用户自定义字面量:""y 用于构造 std::chrono::year 类型的字面量,""d 用于构造 std::chrono::day 类型的字面量。
此外,还有一些常量表示:
- 对于std::chrono::month,常量命名为 January、February,直至 December;
- 对于std::chrono::weekday,常量命名为 Sunday、Monday、Tuesday、Wednesday、Thursday、Friday 或 Saturday。
我们可以使用它们来构造日期,例如 2020y/April/1、25d/December/2020 或 Sunday[last]/May/2020。
year_month_day 类型提供了与 std::chrono::sys_days 之间的隐式转换,该类型是一个 std::chrono::time_point,精度为一天(24 小时)。还有一个称为 std::chrono::sys_seconds 的伴生类型,它是一个精确到 1 秒的 time_point。
time_point 和 sys_days/sys_seconds 之间的显式转换,可以使用 std::chrono::time_point_cast() 或 std::floor() 来执行。
要表示一天中的某个时刻,可以使用 std::chrono::time_of_day 类型,该类型表示从午夜开始经过的时间,以时、分、秒和子秒的形式表示。这个类模板针对不同的精度(std::chrono::hours、std::chrono::minutes 和 std::chrono::seconds)进行了特化。此类型主要用作格式化工具,它有两个名为 make12() 和 make24() 的成员,它们用于将输出的时间格式更改为 12 小时或 24 小时格式。
总结
本节描述的日期和时间工具都基于 std::chrono::system_clock。自 C++20 以来,这个时钟测量 Unix 时间,即自 1970 年 1 月 1 日 00:00:00 UTC 开始的时间。这意味着隐式时区是 UTC。但是,在大多数情况下,人们感兴趣的可能是特定时区的当地时间。为此,chrono 库还增加了对时区的支持。