C++ chrono::duration类的用法(附带实例)
无论使用何种编程语言,处理时间和日期都是一种常见的操作。C++11 提供了一个灵活的日期和时间库,将其作为标准库的一部分,它使我们能够定义时间点和时间间隔。这个库就是 chrono,是一个通用的工具库,用于使用计时器和时钟功能。这些计时器和时钟在不同的系统上可能不同,因此与精度无关。
chrono 库在 std::chrono 命名空间的头文件 <chrono> 中声明,它定义并实现了几个组件,如下所示:
接下来,我们将学习如何使用持续时间。在 chrono 库中,时间间隔由 std::chrono::duration 类表示。
要使用时间间隔,请使用以下方法:
1) std::chrono::duration 类型包含时、分、秒、毫秒、微秒和纳秒:
2) 使用 C++14 中标准的用户自定义字面量操作符(在命名空间 std::chrono_literals 中),创建时、分、秒、毫秒、微秒和纳秒的持续时间:
3) 使用较低精度持续时间到较高精度持续时间的直接转换:
4) 使用 std::chrono::duration_cast 将精度较高的持续时间转换为精度较低的持续时间:
5) 当需要舍入时,使用 C++17 中可用的转换函数 floor()、round() 和 ceil():
6) 使用算术运算、复合赋值和比较操作符来修改和比较时间间隔:
std::chrono::duration 类定义了单位时间内的 tick 数(两个时刻之间的增量),默认单位是秒,对于其他单位(如分或毫秒),我们需要使用比率:
可以使用 count() 成员函数检索 tick 的数量。
标准库为持续时间定义了几个类型同义词,如纳秒、微秒、毫秒、秒、分和时。下面的代码显示了如何在 chrono 命名空间中定义这些持续时间:
在 C++14 中,几个标准的用户自定义字面量操作符被添加到命名空间 std::chrono_literals 中,这使得定义持续时间变得更容易,但你必须在希望使用字面量操作符的作用域中包含命名空间。
为了避免与来自不同库和命名空间的具有相同名称的其他操作符冲突,应该只在希望使用用户定义的字面量操作符的作用域内,而不是在更大的作用域内包含它们的命名空间。
duration 类可以使用所有算术运算,可以进行加或减,可以乘以或除以一个值,也可以应用 modulo 运算。但是,需要注意的是,当两个不同时间单位的持续时间相加或相减时,结果是两个时间单位的最大公约数的持续时间。
例如,如果将一个表示秒的持续时间和一个表示分的持续时间相加,结果将是一个表示秒的持续时间。
将具有较不精确时间单位的持续时间转换为具有较精确时间单位的持续时间是隐式进行的。从较精确的时间单位转换到不太精确的时间单位需要显式强制的转换,这是通过非成员函数 std::chrono::duration_cast() 完成的。
C++17 增加了几个非成员转换函数,这些函数通过四舍五入执行持续时间转换:floor() 向下舍入,ceil() 向上舍入,round() 舍入到最近的值。另外,C++17 还添加了一个名为 abs() 的非成员函数来保留持续时间的绝对值。
C++20 标准增加了对日历和时区的支持,第三方库也可以实现这些特性,推荐的一个库是 Howard Hinnant 的 date 库,这个库是 C++20 chrono 扩展的基础。
chrono 库在 std::chrono 命名空间的头文件 <chrono> 中声明,它定义并实现了几个组件,如下所示:
- 持续时间,表示时间间隔;
- 时间点,表示从时钟的纪元开始的持续时间;
- 时钟,定义了一个纪元(即时间起点)和一个 tick。
接下来,我们将学习如何使用持续时间。在 chrono 库中,时间间隔由 std::chrono::duration 类表示。
要使用时间间隔,请使用以下方法:
1) std::chrono::duration 类型包含时、分、秒、毫秒、微秒和纳秒:
std::chrono::hours half_day(12); std::chrono::minutes half_hour(30); std::chrono::seconds half_minute(30); std::chrono::milliseconds half_second(500); std::chrono::microseconds half_millisecond(500); std::chrono::nanoseconds half_microsecond(500);
2) 使用 C++14 中标准的用户自定义字面量操作符(在命名空间 std::chrono_literals 中),创建时、分、秒、毫秒、微秒和纳秒的持续时间:
using namespace std::chrono_literals; auto half_day = 12h; auto half_hour = 30min; auto half_minute = 30s; auto half_second = 500ms; auto half_millisecond = 500us; auto half_microsecond = 500ns;
3) 使用较低精度持续时间到较高精度持续时间的直接转换:
std::chrono::hours half_day_in_h(12); std::chrono::minutes half_day_in_min(half_day_in_h); std::cout << half_day_in_h.count() << "h" << '\n'; //12h std::cout << half_day_in_min.count() << "min" << '\n';//720min
4) 使用 std::chrono::duration_cast 将精度较高的持续时间转换为精度较低的持续时间:
using namespace std::chrono_literals; auto total_seconds = 12345s; auto hours = std::chrono::duration_cast<std::chrono::hours>(total_seconds); auto minutes = std::chrono::duration_cast<std::chrono::minutes>(total_seconds % 1h); auto seconds = std::chrono::duration_cast<std::chrono::seconds>(total_seconds % 1min); std::cout << hours.count() << ':' << minutes.count() << ' ' << seconds.count() << '\n'; // 3:25:45
5) 当需要舍入时,使用 C++17 中可用的转换函数 floor()、round() 和 ceil():
using namespace std::chrono_literals; auto total_seconds = 123455s; auto m1 = std::chrono::floor<std::chrono::minutes>(total_seconds); // 205 min auto m2 = std::chrono::round<std::chrono::minutes>(total_seconds); // 206 min auto m3 = std::chrono::ceil<std::chrono::minutes>(total_seconds); // 206 min auto sa = std::chrono::abs(total_seconds);
6) 使用算术运算、复合赋值和比较操作符来修改和比较时间间隔:
using namespace std::chrono_literals; auto d1 = 1h + 23min + 45s; // d1 = 50255 auto d2 = 3h + 12min + 50s; // d2 = 11570s if (d1 < d2) { /* do something */ }
std::chrono::duration 类定义了单位时间内的 tick 数(两个时刻之间的增量),默认单位是秒,对于其他单位(如分或毫秒),我们需要使用比率:
- 对于大于秒的单位,比率大于 1,例如 ratio<60> (用于单位分);
- 对于小于秒的单位,比率小于 1,例如 ratio<1, 1000>(用于单位毫秒)。
可以使用 count() 成员函数检索 tick 的数量。
标准库为持续时间定义了几个类型同义词,如纳秒、微秒、毫秒、秒、分和时。下面的代码显示了如何在 chrono 命名空间中定义这些持续时间:
namespace std { namespace chrono { typedef duration<long long long, ratio<1, 1000000000000>> nanoseconds; typedef duration<long long long, ratio<1, 1000000>> microseconds; typedef duration<long long, ratio<1, 1000>> milliseconds; typedef duration<long long> seconds; typedef duration<int, ratio<60> minutes; typedef duration<int, ratio<3600> > hours; } }然而,通过这种灵活的定义,我们可以表示时间间隔,例如 1.2 /6 分(即 12 秒),其中 1.2 是持续时间的 tick 数,而 ratio<10>(即 60/6)是时间单位:
std::chrono::duration<double, std::ratio<1>> d(1.2); // 12 sec
在 C++14 中,几个标准的用户自定义字面量操作符被添加到命名空间 std::chrono_literals 中,这使得定义持续时间变得更容易,但你必须在希望使用字面量操作符的作用域中包含命名空间。
为了避免与来自不同库和命名空间的具有相同名称的其他操作符冲突,应该只在希望使用用户定义的字面量操作符的作用域内,而不是在更大的作用域内包含它们的命名空间。
duration 类可以使用所有算术运算,可以进行加或减,可以乘以或除以一个值,也可以应用 modulo 运算。但是,需要注意的是,当两个不同时间单位的持续时间相加或相减时,结果是两个时间单位的最大公约数的持续时间。
例如,如果将一个表示秒的持续时间和一个表示分的持续时间相加,结果将是一个表示秒的持续时间。
将具有较不精确时间单位的持续时间转换为具有较精确时间单位的持续时间是隐式进行的。从较精确的时间单位转换到不太精确的时间单位需要显式强制的转换,这是通过非成员函数 std::chrono::duration_cast() 完成的。
C++17 增加了几个非成员转换函数,这些函数通过四舍五入执行持续时间转换:floor() 向下舍入,ceil() 向上舍入,round() 舍入到最近的值。另外,C++17 还添加了一个名为 abs() 的非成员函数来保留持续时间的绝对值。
总结
chrono 是一个通用库,在 C++20 之前,它缺乏许多有用的特性,比如用年、月、日部分表示日期,使用时区和日历等。C++20 标准增加了对日历和时区的支持,第三方库也可以实现这些特性,推荐的一个库是 Howard Hinnant 的 date 库,这个库是 C++20 chrono 扩展的基础。