C++处理日期和时间的chrono库
C++11 中提供了日期和时间相关的库 chrono,通过 chrono 库可以很方便地处理日期和时间,为程序的开发提供了便利。chrono 库主要包含三种类型的类:时间间隔duration、时钟clocks、时间点time point。
时间间隔 duration
常用类成员
duration表示一段时间间隔,用来记录时间长度,可以表示几秒、几分钟、几个小时的时间间隔。duration 的原型如下:
// 定义于头文件 <chrono>
template<class Rep,class Period = std::ratio<1>
> class duration;
Rep:这是一个数值类型,表示时钟数(周期)的类型(默认为整形)。若 Rep 是浮点数,则 duration 能使用小数描述时钟周期的数目。
Period:表示时钟的周期,它的原型如下:
// 定义于头文件 <ratio>
template<std::intmax_t Num,std::intmax_t Denom = 1> class ratio;
ratio 类表示每个时钟周期的秒数,其中第一个模板参数 Num代表分子,Denom代表分母,该分母值默认为 1,因此,ratio代表的是一个分子除以分母的数值,比如:ratio<2> 代表一个时钟周期是 2 秒,ratio<60 > 代表一分钟,ratio<6060 > 代表一个小时,ratio<6060*24 > 代表一天。而 ratio<1,1000 > 代表的是 1/1000 秒,也就是 1 毫秒,ratio<1,1000000 > 代表一微秒,ratio<1,1000000000 > 代表一纳秒。
为了方便使用,在标准库中定义了一些常用的时间间隔,比如:时、分、秒、毫秒、微秒、纳秒,它们都位于 chrono 命名空间下,定义如下:
类型 | 定义 |
---|---|
纳秒: | std::chrono::nanoseconds duration<Rep*/* 至少 64 位的有符号整数类型 /, std::nano> |
微秒: | std::chrono::microseconds duration<Rep*/* 至少 55 位的有符号整数类型 /, std::micro> |
毫秒: | std::chrono::milliseconds duration<Rep*/* 至少 45 位的有符号整数类型 /, std::milli> |
秒: | std::chrono::seconds duration<Rep*/* 至少 35 位的有符号整数类型 /> |
分钟: | std::chrono::minutes duration<Rep*/* 至少 29 位的有符号整数类型 /, std::ratio<60>> |
小时: | std::chrono::hours duration<Rep*/* 至少 23 位的有符号整数类型 /, std::ratio<3600>> |
注意:到 hours 为止的每个预定义时长类型至少涵盖 ±292 年的范围。
duration 类的构造函数原型如下:
// 1. 拷贝构造函数
duration( const duration& ) = default;
// 2. 通过指定时钟周期的类型来构造对象
template< class Rep2 >
constexpr explicit duration( const Rep2& r );
// 3. 通过指定时钟周期类型,和时钟周期长度来构造对象
template< class Rep2, class Period2 >
constexpr duration( const duration<Rep2,Period2>& d );
为了更加方便的进行 duration 对象之间的操作,类内部进行了操作符重载:
操作符重载 | 描述 |
---|---|
operator= | 赋值内容 (公开成员函数) |
operator+ | |
operator- | 实现一元 + 和一元 - (公开成员函数) |
operator++ | |
operator++(int) | |
operator– | |
operator–(int) | 递增或递减周期计数 (公开成员函数) |
operator+= | |
operator-= | |
operator*= | |
operator/= | |
operator%= | 实现二个时长间的复合赋值 (公开成员函数) |
duration 类还提供了获取时间间隔的时钟周期数的方法 count (),函数原型如下:
constexpr rep count() const;
类的使用
构造函数
int main()
{//构造函数//一小时chrono::hours h(1);//3毫秒chrono::milliseconds{ 3 };//3000秒chrono::duration<int, ratio<1000>> ks(3);//6.6秒chrono::duration<double> dd(6.6);//是用小数表示时钟周期的次数 时钟周期为 1/30 秒,共有 3.5 个时钟周期,所以 hz 表示的时间间隔为 1/30*3.5 秒chrono::duration<double, std::ratio<1, 30>> hz(3.5);}
chrono 库中根据 duration 类封装了不同长度的时钟周期(也可以自定义),基于这个时钟周期再进行周期次数的设置就可以得到总的时间间隔了(时钟周期 * 周期次数 = 总的时间间隔)。
int main()
{//3毫秒chrono::milliseconds ms{ 3 };//6000微妙chrono::microseconds us = 2 * ms;//时间间隔周期为1/30秒std::chrono::duration<double, std::ratio<1, 30>> hz(3.54);std::cout << "3 ms duration has " << ms.count() << " ticks\n"<< "6000 us duration has " << us.count() << " ticks\n"<< "3.5 hz duration has " << hz.count() << " ticks\n";}
由于在 duration 类内部做了操作符重载,因此时间间隔之间可以直接进行算术运算,比如我们要计算两个时间间隔的差值,就可以在代码中做如下处理:
#include <iostream>
#include <chrono>
using namespace std;int main()
{chrono::minutes t1(10);chrono::seconds t2(60);//540chrono::seconds t3 = t1 - t2;cout << t3.count() << " second" << endl;
}
注意事项:duration 的加减运算有一定的规则,当两个 duration 时钟周期不相同的时候,会先统一成一种时钟,然后再进行算术运算,统一的规则如下:假设有 ratio<x1,y1> 和 ratio<x2,y2 > 两个时钟周期,首先需要求出 x1,x2 的最大公约数 X,然后求出 y1,y2 的最小公倍数 Y,统一之后的时钟周期 ratio 为 ratio<X,Y>。
时间点 time point
chrono 库中提供了一个表示时间点的类 time_point,该类的定义如下:
// 定义于头文件 <chrono>
template<class Clock,class Duration = typename Clock::duration
> class time_point;
它被实现成如同存储一个 Duration 类型的自 Clock 的纪元起始开始的时间间隔的值,通过这个类最终可以得到时间中的某一个时间点。
Clock:此时间点在此时钟上计量
Duration:用于计量从纪元起时间的 std::chrono::duration 类型
time_point 类的构造函数原型如下:
// 1. 构造一个以新纪元(epoch,即:1970.1.1)作为值的对象,需要和时钟类一起使用,不能单独使用该无参构造函数
time_point();
// 2. 构造一个对象,表示一个时间点,其中d的持续时间从epoch开始,需要和时钟类一起使用,不能单独使用该构造函数
explicit time_point( const duration& d );
// 3. 拷贝构造函数,构造与t相同时间点的对象,使用的时候需要指定模板参数
template< class Duration2 >
time_point( const time_point<Clock,Duration2>& t );
在这个类中除了构造函数还提供了另外一个 time_since_epoch() 函数,用来获得 1970 年 1 月 1 日到 time_point 对象中记录的时间经过的时间间隔(duration),函数原型如下:
duration time_since_epoch() const;
除此之外,时间点 time_point 对象和时间段对象 duration 之间还支持直接进行算术运算(即加减运算),时间点对象之间可以进行逻辑运算,具体细节可以参考下面的表格:
其中 tp 和 tp2 是 time_point 类型的对象, dtn 是 duration 类型的对象。
描述 | 操作 | 返回值 |
---|---|---|
复合赋值 (成员函数) | operator+= tp += dtn | *this |
复合赋值 (成员函数) | operator-= tp -= dtn | *this |
算术运算符 (非成员函数) | operator+ tp + dtn | a time_point value |
算术运算符 (非成员函数) | operator+ dtn + tp | atime_point value |
算术运算符 (非成员函数) | operator- tp - dtn | a time_point value |
算术运算符 (非成员函数) | operator- tp - tp2 | aduration value |
关系操作符 (非成员函数) | operator== tp == tp2 | a bool value |
关系操作符 (非成员函数) | operator!= tp != tp2 | a bool value |
关系操作符 (非成员函数) | operator< tp < tp2 | a bool value |
关系操作符 (非成员函数) | operator> tp > tp2 | a bool value |
关系操作符 (非成员函数) | operator>= tp >= tp2 | a bool value |
关系操作符 (非成员函数) | operator<= tp <= tp2 | a bool value |
由于该时间点类经常和下面要介绍的时钟类一起使用,所以在此先不举例,在时钟类的示例代码中会涉及到时间点类的使用,到此为止只需要搞明白时间点类的提供的这几个函数的作用就可以了。
时钟 clocks
chrono 库中提供了获取当前的系统时间的时钟类,包含的时钟一共有三种:
system_clock:系统的时钟,系统的时钟可以修改,甚至可以网络对时,因此使用系统时间计算时间差可能不准。
steady_clock:是单调的时钟,相当于秒表。开始计时后,时间只会增长并且不能修改,适合用于记录程序耗时
high_resolution_clock:和时钟类 steady_clock 是等价的(是它的别名)。
在这些时钟类的内部有 time_point、duration、Rep、Period 等信息,基于这些信息来获取当前时间,以及实现 time_t 和 time_point 之间的相互转换。
时钟类成员类型 | 描述 |
---|---|
rep | 表示时钟周期次数的有符号算术类型 |
period | 表示时钟计次周期的 std::ratio 类型 |
duration | 时间间隔,可以表示负时长 |
time_point | 表示在当前时钟里边记录的时间点 |
在使用chrono提供的时钟类的时候,不需要创建类对象,直接调用类的静态方法就可以得到想要的时间了。
system_clock
具体来说,时钟类 system_clock 是一个系统范围的实时时钟。system_clock 提供了对当前时间点 time_point 的访问,将得到时间点转换为 time_t 类型的时间对象,就可以基于这个时间对象获取到当前的时间信息了。
system_clock 时钟类在底层源码中的定义如下:
struct system_clock { // wraps GetSystemTimePreciseAsFileTime/GetSystemTimeAsFileTimeusing rep = long long;using period = ratio<1, 10'000'000>; // 100 nanosecondsusing duration = chrono::duration<rep, period>;using time_point = chrono::time_point<system_clock>;static constexpr bool is_steady = false;_NODISCARD static time_point now() noexcept { // get current timereturn time_point(duration(_Xtime_get_ticks()));}_NODISCARD static __time64_t to_time_t(const time_point& _Time) noexcept { // convert to __time64_treturn duration_cast<seconds>(_Time.time_since_epoch()).count();}_NODISCARD static time_point from_time_t(__time64_t _Tm) noexcept { // convert from __time64_treturn time_point{seconds{_Tm}};}
};
通过以上源码可以了解到在 system_clock 类中的一些细节信息:
rep:时钟周期次数是通过整形来记录的 long long
period:一个时钟周期是 100 纳秒 ratio<1, 10’000’000>
duration:时间间隔为 rep*period 纳秒 chrono::duration<rep, period>
time_point:时间点通过系统时钟做了初始化 chrono::time_point<system_clock>,里面记录了新纪元时间点
另外还可以看到 system_clock 类一共提供了三个静态成员函数:
// 返回表示当前时间的时间点。
static std::chrono::time_point<std::chrono::system_clock> now() noexcept;
// 将 time_point 时间点类型转换为 std::time_t 类型
static std::time_t to_time_t( const time_point& t ) noexcept;
// 将 std::time_t 类型转换为 time_point 时间点类型
static std::chrono::system_clock::time_point from_time_t( std::time_t t ) noexcept;
比如,我们要获取当前的系统时间,并且需要将其以能够识别的方式打印出来,示例代码如下:
#include <chrono>
#include <iostream>
using namespace std;
using namespace std::chrono;
int main()
{// 新纪元1970.1.1时间system_clock::time_point epoch;duration<int, ratio<60*60*24>> day(1);// 新纪元1970.1.1时间 + 1天system_clock::time_point ppt(day);using dday = duration<int, ratio<60 * 60 * 24>>;// 新纪元1970.1.1时间 + 10天time_point<system_clock, dday> t(dday(10));// 系统当前时间system_clock::time_point today = system_clock::now();// 转换为time_t时间类型time_t tm = system_clock::to_time_t(today);cout << "今天的日期是: " << ctime(&tm);time_t tm1 = system_clock::to_time_t(today+day);cout << "明天的日期是: " << ctime(&tm1);time_t tm2 = system_clock::to_time_t(epoch);cout << "新纪元时间: " << ctime(&tm2);time_t tm3 = system_clock::to_time_t(ppt);cout << "新纪元时间+1天: " << ctime(&tm3);time_t tm4 = system_clock::to_time_t(t);cout << "新纪元时间+10天: " << ctime(&tm4);
}
steady_clock
如果我们通过时钟不是为了获取当前的系统时间,而是进行程序耗时的时长,此时使用 syetem_clock 就不合适了,因为这个时间可以跟随系统的设置发生变化。在 C++11 中提供的时钟类 steady_clock 相当于秒表,只要启动就会进行时间的累加,并且不能被修改,非常适合于进行耗时的统计。
steady_clock 时钟类在底层源码中的定义如下:
struct steady_clock { // wraps QueryPerformanceCounterusing rep = long long;using period = nano;using duration = nanoseconds;using time_point = chrono::time_point<steady_clock>;static constexpr bool is_steady = true;// get current time_NODISCARD static time_point now() noexcept { // doesn't change after system bootconst long long _Freq = _Query_perf_frequency(); const long long _Ctr = _Query_perf_counter();static_assert(period::num == 1, "This assumes period::num == 1.");const long long _Whole = (_Ctr / _Freq) * period::den;const long long _Part = (_Ctr % _Freq) * period::den / _Freq;return time_point(duration(_Whole + _Part));}
};
过以上源码可以了解到在 steady_clock 类中的一些细节信息:
rep:时钟周期次数是通过整形来记录的 long long
period:一个时钟周期是 1 纳秒 nano
duration:时间间隔为 1 纳秒 nanoseconds
time_point:时间点通过系统时钟做了初始化 chrono::time_point<steady_clock>,里面记录了新纪元时间点
另外,在这个类中也提供了一个静态的 now () 方法,用于得到当前的时间点,函数原型如下:
static std::chrono::time_point<std::chrono::steady_clock> now() noexcept;
假设要测试某一段程序的执行效率,可以计算它执行期间消耗的总时长,示例代码如下:
#include <chrono>
#include <iostream>
using namespace std;
using namespace std::chrono;
int main()
{// 获取开始时间点steady_clock::time_point start = steady_clock::now();// 执行业务流程cout << "print 1000 stars ...." << endl;for (int i = 0; i < 1000; ++i){cout << "*";}cout << endl;// 获取结束时间点steady_clock::time_point last = steady_clock::now();// 计算差值auto dt = last - start;cout << "总共耗时: " << dt.count() << "纳秒" << endl;
}
high_resolution_clock
high_resolution_clock 提供的时钟精度比 system_clock 要高,它也是不可以修改的。在底层源码中,这个类其实是 steady_clock 类的别名。
using high_resolution_clock = steady_clock;
因此 high_resolution_clock 的使用方式和 steady_clock 是一样的,在此就不再过多进行赘述了。
转换函数
duration_cast
duration_cast 是 chrono 库提供的一个模板函数,这个函数不属于 duration 类。通过这个函数可以对 duration 类对象内部的时钟周期 Period,和周期次数的类型 Rep 进行修改,该函数原型如下:
template <class ToDuration, class Rep, class Period>constexpr ToDuration duration_cast (const duration<Rep,Period>& dtn);
在源周期能准确地为目标周期所整除的场合(例如小时到分钟),浮点时长和整数时长间转型能隐式进行无需 duration_cast ,其他情况下都需要通过函数进行转换。
我们可以修改一下上面测试程序执行时间的代码,在代码中修改 duration 对象的属性:
#include <iostream>
#include <chrono>
using namespace std;
using namespace std::chrono;void f()
{cout << "print 1000 stars ...." << endl;for (int i = 0; i < 1000; ++i){cout << "*";}cout << endl;
}int main()
{auto t1 = steady_clock::now();f();auto t2 = steady_clock::now();// 整数时长:要求 duration_castauto int_ms = duration_cast<chrono::milliseconds>(t2 - t1);// 小数时长:不要求 duration_castduration<double, ratio<1, 1000>> fp_ms = t2 - t1;cout << "f() took " << fp_ms.count() << " ms, "<< "or " << int_ms.count() << " whole milliseconds\n";
}
time_point_cast
time_point_cast 也是 chrono 库提供的一个模板函数,这个函数不属于 time_point 类。函数的作用是对时间点进行转换,因为不同的时间点对象内部的时钟周期 Period,和周期次数的类型 Rep 可能也是不同的,一般情况下它们之间可以进行隐式类型转换,也可以通过该函数显示的进行转换,函数原型如下:
template <class ToDuration, class Clock, class Duration>
time_point<Clock, ToDuration> time_point_cast(const time_point<Clock, Duration> &t);
#include <chrono>
#include <iostream>
using namespace std;using Clock = chrono::high_resolution_clock;
using Ms = chrono::milliseconds;
using Sec = chrono::seconds;
template<class Duration>
using TimePoint = chrono::time_point<Clock, Duration>;void print_ms(const TimePoint<Ms>& time_point)
{std::cout << time_point.time_since_epoch().count() << " ms\n";
}int main()
{TimePoint<Sec> time_point_sec(Sec(6));// 无精度损失, 可以进行隐式类型转换TimePoint<Ms> time_point_ms(time_point_sec);print_ms(time_point_ms); // 6000 mstime_point_ms = TimePoint<Ms>(Ms(6789));// error,会损失精度,不允许进行隐式的类型转换TimePoint<Sec> sec(time_point_ms);// 显示类型转换,会损失精度。6789 truncated to 6000time_point_sec = std::chrono::time_point_cast<Sec>(time_point_ms);print_ms(time_point_sec); // 6000 ms
}
注意事项:关于时间点的转换如果没有没有精度的损失可以直接进行隐式类型转换,如果会损失精度只能通过显示类型转换,也就是调用 time_point_cast 函数来完成该操作。
C++处理日期和时间的chrono库相关推荐
- 处理日期和时间的 chrono 库
C++11 中提供了日期和时间相关的库 chrono,通过 chrono 库可以很方便地处理日期和时间,为程序的开发提供了便利.chrono 库主要包含三种类型的类:时间间隔duration.时钟cl ...
- Rust 日期与时间之chrono
特别说明 本文以一个Java-1.8 开发者视角学习 Rust中的日期与时间. Rust 版本: 1.8 由于Rust std 库里面的时间大多不好用,本文的示例都是基于chrono库. 本文的示例代 ...
- 10个用于处理日期和时间的 Python 库
Python本身提供了处理时间日期的功能,也就是datetime标准库.除此之外,还有很多优秀的第三方库可以用来转换日期格式,格式化,时区转化等等.今天就给大家分享10个这样的Python库. *** ...
- 时间编程Chrono库 - C++11
chrono库主要包含了三种类型:时间间隔Duration.时钟Clocks和时间点Time point.时钟是系统内部不断循环执行的时钟,某个一点对应的是时间点timepoint,两个时间点之间的差 ...
- C++11新特性——时间操作chrono库
此课件及源代码来自B站up主:码农论坛,该文章仅作为本人学习笔记使用. C++11提供了chrono模版库,实现了一系列时间相关的操作(时间长度.系统时间和计时器). 头文件:#include < ...
- python时间库_Python处理日期时间的标准库:time和datetime
本文的文字及图片来源于网络,仅供学习.交流使用,不具有任何商业用途,版权归原作者所有,如有问题请及时联系我们以作处理 以下文章来源于Python之王 ,作者小sen 前言 time和datetime是 ...
- poco,c++库学习,日期和时间
POCO C++库学习和分析 -- 日期与时间 在Poco库中,与时间和日期相关的一些类,其内部实现是非常简单的.看相关文档时,比较有意思的倒是历史上的不同时间表示法. 1. 系统时间函数 ...
- C++11中的chrono库
文章目录 前言 1.记录时长的duration 2.表示时间点的time_point 3.获取系统时钟的clocks 前言 C++11提供了日期时间相关的库chrono,通过chrono库可以很方便的 ...
- python中处理日期和时间的标准模块是-datetime
datetime是Python处理日期和时间的标准库. 获取当前日期和时间 我们先看如何获取当前日期和时间: >>> from datetime import datetime &g ...
最新文章
- 从零开始学习jQuery (六) AJAX快餐【转】
- QuickTime 0day ***代码发布,可能允许执行任意代码
- Google总裁:未来互联网要消失!物联网将无处不在。
- mysql子查询教行子查询_MySQL中列子查询与行子查询操作的学习教程
- Spring Boot中使用log4j实现http请求日志入mongodb
- cos大乱斗服务器维护,《COS大乱斗》服务器数据互通公告
- Android Studio小技巧
- ubuntu14.04+ROS indigo+kinectV1 骨骼点检测
- 互联网金融数据分析-笔记
- python method与function方法_学编程这么久,还傻傻分不清什么是方法(method),什么是函数(function)?...
- mysql5.6.27安装_mysql-5.6.27源码安装及错误解决办法
- python中的math.floor可以用于整数吗_为什么Python的math.ceil()和math.floor()操作返回浮点数而不是整数?...
- 迁移到MySQL的语法转换工具初步设计
- “知识资源细颗粒度建设和标签标引”规则规范心得说明
- 502 bad gateway
- html中背景简写,css中background简写属性
- 教你如何在家中自建服务器
- 好用的区块链浏览器--BlockScout安装
- chia 免费软件快速p图 支持官方最新协议
- 视频剪辑工具:剪映专业版 for Mac
热门文章
- 揭秘:各大视频网站是这样密谋秀场的
- Java语言程序设计教程(魏永红版)第8章课本源码
- zookeeper启动报mkdir: 无法创建目录““: 没有那个文件或目录
- 在SOLIDWORKS装配体中显示装饰性螺纹线的小技巧!
- 电骡协议规范-第二章-客户端和服务器TCP通讯
- 开通抖音小程序tt.openEcGood功能
- 90%的人都有的图表使用误区,赶紧看看自己是否中招
- Type-c PD QC AFC取电芯片 快充芯片LDR6328S
- 硕士生一字不差抄袭顶会论文投稿,北理工:严重学术不端,留校察看
- P4779 【模板】单源最短路径(标准版)