C++11中提供了日期和时间相关的库chrono,通过chrono库可以很方便地处理日期和时间,为程序的开发提供了便利。chrono库主要包含三种类型的类:时间间隔duration、时钟clocks、时间点time point。
一.时间间隔
1.原型
duration表示一段时间间隔,用来记录时间长度,可以表示几秒、几分钟、几个小时的时间间隔。duration的原型如下:
// 定义于头文件 <chrono>
template<
class Rep,
class Period = std::ratio<1>
> class duration;
-
Rep:这是一个数值类型,表示时钟数(周期)的类型(默认为整形)。若Rep是浮点数,则duration能使用小数描述时钟周期的数目。 -
Period:表示时间单位,是一个std::ratio类型,用于表示时间单位与秒的比率,它的原型如下:// 定义于头文件 <ratio> template< std::intmax_t Num, std::intmax_t Denom = 1 > class ratio;ratio类表示每个时钟周期的秒数,其中第一个模板参数Num代表分子,Denom代表分母,该分母值默认为1,因此,ratio代表的是一个分子除以分母的数值,比如:ratio<2>代表一个时钟周期是2秒,ratio<60>代表一分钟,ratio<60*60>代表一个小时,ratio<60*60*24>代表一天。而ratio<1,1000>代表的是1/1000秒,也就是1毫秒,ratio<1,1000000>代表一微秒,ratio<1,1000000000>代表一纳秒。为了方便使用,在标准库中定义了一些常用的时间间隔,比如:时、分、秒、毫秒、微秒、纳秒,它们都位于chrono命名空间下,定义如下:
类型 定义 纳秒: std::chrono::nanoseconds微秒: std::chrono::microseconds毫秒: std::chrono::milliseconds秒: std::chrono::seconds分钟: std::chrono::minutes小时: std::chrono::hours
2.构造函数
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 );
3.基本使用
通过构造函数构造事件间隔对象示例代码如下:
#include <chrono>
#include <iostream>
using namespace std;
int main()
{
chrono::hours h(1); // 一小时
chrono::milliseconds ms{ 3 }; // 3 毫秒
chrono::duration<int, ratio<1000>> ks(3); // 3000 秒
// chrono::duration<int, ratio<1000>> d3(3.5); // error
chrono::duration<double> dd(6.6); // 6.6 秒
// 使用小数表示时钟周期的次数
chrono::duration<double, std::ratio<1, 30>> hz(3.5);
}
chrono库中根据duration类封装了不同长度的时钟周期(也可以自定义),基于这个时钟周期再进行周期次数的设置就可以得到总的时间间隔了(时钟周期 * 周期次数 = 总的时间间隔)。
补充:
duration类还提供了获取时间间隔的时钟周期数的方法count()
由于在duration类内部做了操作符重载,因此时间间隔之间可以直接进行算术运算,比如我们要计算两个时间间隔的差值,就可以在代码中做如下处理:
#include <iostream>
#include <chrono>
using namespace std;
int main()
{
chrono::minutes t1(10);
chrono::seconds t2(60);
chrono::seconds t3 = t1 - t2;
cout << t3.count() << " second" << endl;
}
测试程序中,t1代表10分钟,t2代表60秒,t3是t1减去t2,也就是60*10-60=540,这个540表示的时钟周期,每个时钟周期是1秒,因此两个时间间隔之间的差值为540秒。
注意事项:
duration的加减运算有一定的规则,当两个duration时钟周期不相同的时候,会先统一成一种时钟,然后再进行算术运算,统一的规则如下:假设有ratio<x1,y1>和ratio<x2,y2>两个时钟周期,首先需要求出x1,x2的最大公约数X,然后求出y1,y2的最小公倍数Y,统一之后的时钟周期ratio为ratio<X,Y>。
4.其他常见成员函数
zero:静态成员常量,返回一个值为零的duration。duration_cast:一个模板函数,用于在duration类型之间进行安全的转换。
// 使用预定义的时间单位
std::chrono::milliseconds ms(1000);
std::cout << "时间间隔为: " << ms.count() << " 毫秒" << std::endl;
// 使用duration_cast进行类型转换
auto seconds = std::chrono::duration_cast<std::chrono::seconds>(ms);
std::cout << "转换为秒: " << seconds.count() << " 秒" << std::endl;
// zero - 返回一个值为零的 duration
auto zero_duration = std::chrono::duration<int>::zero();
std::cout << "零值 duration: " << zero_duration.count() << " 秒" << std::endl;
二.时间点
1.原型
chrono库中提供了一个表示时间点的类time_point,该类的定义如下:
// 定义于头文件 <chrono>
template<
class Clock, //匹配的时钟
class Duration = typename Clock::duration //与对应时钟起始点之间的时间间隔
> class time_point;
time_point 是一个模板类,用于表示时间线上的一个点。它通常与特定的时钟类型(如 system_clock、steady_clock 或 high_resolution_clock)相关联,并且与一个 duration 类型相关,该类型表示时间点与相关时钟的起始点(epoch)之间的时间间隔。
2.构造函数
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)。
此之外,时间点time_point对象和时间段对象duration之间还支持直接进行算术运算(即加减运算),时间点对象之间可以进行逻辑运算。具体使用的例子参考下面的时钟类:
三.时钟clocks
1.常见时钟
chrono库中提供了获取当前的系统时间的时钟类,包含的时钟一共有三种:
system_clock:系统的时钟,系统的时钟可以修改,甚至可以网络对时,因此使用系统时间计算时间差可能不准;steady_clock:是固定的时钟,相当于秒表。开始计时后,时间只会增长并且不能修改,适合用于记录程序耗时;high_resolution_clock:提供高精度的计时功能,通常是与系统时钟或steady_clock相同或更高精度的实现。
在这些时钟类的内部有time_point、duration、Rep、Period等信息,基于这些信息来获取当前时间,以及实现time_t和time_point之间的相互转换。
在使用
chrono提供的时钟类的时候,不需要创建类对象,直接调用类的静态方法就可以得到想要的时间了。
2.系统时钟
可以看到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); //day是1天的时间间隔
cout << "明天的日期是: " << ctime(&tm1);
time_t tm2 = system_clock::to_time_t(epoch); //起始时间是1970.1.1的8:00
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);
}
这里仔细体会,注意在 C++11 的 <chrono> 库中,您可以使用 operator+= 运算符来在 time_point 上添加一个 duration。例如,要向 epoch 添加一天,您可以这样做:
system_clock::time_point new_time = epoch + day;
而不是上述代码的system_clock::time_point ppt(day);难以理解。
3.steady_clock秒表
如果我们通过时钟不是为了获取当前的系统时间,而是进行程序耗时的时长,此时使用syetem_clock就不合适了,因为这个时间可以跟随系统的设置发生变化。在C++11中提供的时钟类steady_clock相当于秒表,只要启动就会进行时间的累加,并且不能被修改,非常适合于进行耗时的统计。
在这个类中也提供了一个静态的
now()方法,用于得到当前的时间点
基本使用:
#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;
}
4.high_resolution_clock高级秒表
high_resolution_clock提供的时钟精度比system_clock要高,它也是不可以修改的。在底层源码中,这个类其实是steady_clock类的别名。
using high_resolution_clock = steady_clock;
因此high_resolution_clock的使用方式和steady_clock是一样的,在此就不再过多进行赘述了。

被折叠的 条评论
为什么被折叠?



