文章目录
跨平台时间处理
对于时间处理的需求
- 获取当前时间,比如记录日志,精度一般到毫秒
- 计算时间间隔,比如判断代码块的运行时长
- 获取日期,日期换算
- 定时器
OS提供的API
- 时间日期相关
time_t time() //返回从1970-01-01 00:00:00 +0000 (UTC)的秒数(C语言标准库提供)
int gettimeofday(struct timeval *tv, struct timezone *tz); //返回指定时区 回从1970-01-01 00:00:00 +0000 (UTC)的秒数及微秒
其中time()
是跨平台的,现在已经在std标准中了,gettimeofday为linux系统特有,在windows平台并没有这个API。显然不同OS都会提供特有的处理时间的API。
- 定时相关
一种跨平台的定时器,是通过select的timeout参数实现。
所以如果要实现跨平台,高精度时间及定时处理,是依赖于特定平台的
日期处理更多的是业务层代码中,时间的处理在底层,在这篇文章中是介绍是使用chrono
时间处理API
C++ 11中时间和日期的处理
包括两种方式:
- C语言风格的方式,在time.h头文件包括一系列的API,在C++11中是被包含在ctime头文件中
- time() 获取时间,用秒数表示
- ctime() 传入一个 time_t类型,返回日期字符串
- localtime 传入一个time_t类型,获取一个tm结构体,其中包括了年,月,日,时,分,秒
精度是到秒
- C++ 11引入的chrono库
精度高,最高精度是到1纳秒
C++ 11中的chrono
chrono的出现也正是为了解决跨平台时间处理这一繁琐的问题,它提供的功能覆盖了上面所提出的前三点时间处理需求,但遗憾的是它并没有包含定时器。
它引入了三个基本的概念:时间点,时间间隔,时间(日期)。时间处理的都是基于这三个概念。
看看C++ reference中的对三个概念的解释
Durations(时间间隔)
They measure time spans, like: one minute, two hours, or ten milliseconds.
Time points(时间点)
A reference to a specific point in time, like one’s birthday, today’s dawn, or when the next train passes.
Clocks(时间)
A framework that relates a time point to real physical time.时间点的通俗概念,包含了时间点,时间间隔,日期
Clock(时间/时钟)
Clock 跟time_t time()
返回值的意义类似,代表这从时间纪元(1970-01-01 00:00:00 +0000),到现在的"距离",这个"距离"有不同的精度。
C++ 11中包含三种clock类型:system_clock
,steady_clok
,high_resolution_clock
,分别对应了不同的精度。并且每一种clock中都定义了特定的duration
及time_point
类型(因为精度不同)
system_clock::duration,system_clock::time_point
steady_clock::duration,steady_clock::time_point
high_resolution_clock::duration,high_resolution_clock::time_point
system_clock
system_clock is useful when you need to correlate the time with a known epoch so you can convert it to a calendar time
system_clock
为系统时间(壁钟时间),它的原点时间是UNIX 1979-01-01 00:00::00 UTC,它包括3个静态方法:now
,to_time_t
,from_time_t
(与C 风格的时间相互转换) 用法比较简单,当需要获取时间时,就可以使用system_clock
- 基本用法
#include <chrono>
#include <iostream>
#include <ctime>
using namespace std::chrono;
int main()
{
//获取当前时间
auto tp = std::chrono::system_clock::now();
std::cout << tp.time_since_epoch().count() << std::endl;
std::cout << "time "<<time(NULL) << std::endl;
//转换为time_t
time_t now = std::chrono::system_clock::to_time_t(tp);
std::cout << "当前时间 " << ctime(&now) << std::endl;
//格式化时间
tm *info = localtime(&now);
char timefmt[128] = { 0 };
sprintf(timefmt, "%d-%d-%d %d:%d:%d", info->tm_year + 1900, info->tm_mon + 1, info->tm_mday, info->tm_hour, info->tm_min, info->tm_sec);
std::cout << timefmt << std::endl;
}
system_clock::now()返回一个时间点类型,通过to_time_t转为从1970 01-01 00:00:00到当前的计数(计数周期是有由period成员指定),再通过localtime
格式化输出,但是通过该函数转换的时候,是将精度丢失了,转换后的精度是秒。
- system_clock 也可以将任意时间点转换成时间
#include <chrono>
#include <iostream>
#include <ctime>
using namespace std::chrono;
int main()
{
//system_clock的精度是100纳秒
system_clock::duration d(10000000);
std::cout << system_clock::duration::period::den << std::endl;
system_clock::time_point epoch(d);
time_t tmEpoch = std::chrono::system_clock::to_time_t(epoch);
std::cout << "value is " << ctime(&tmEpoch) << std::endl;
std::system("pause");
}
- 如果改了系统时间,
system_clock
的值也会变化,所以它是不"稳定的",system_clock::is_steady
是false
steady_clock
steady_clock is useful when you need to wait for a specific amount of time. steady_clock time can not be reset. As other steady clocks, it is usually based on the processor tick.
-
steady_clock
是处理器的tick时间(非壁钟时间),steady_clock::now()
返回的值绝不会小于上一次调用该方法时的值,即使改了系统时间,也会保证steady_clock::now产生正确的值 -
它最适合度量间隔(精度)。它只有一个静态
now
方法,它经常被用来计算时间间隔,通过使用steady_clock域的duration和time_point,来表示高精度的时间间隔
#include <chrono>
#include <iostream>
#include <thread>
using namespace std::chrono;
int main()
{
//时间点
steady_clock::time_point s = steady_clock::now();
std::this_thread::sleep_for(milliseconds(10));
//时间点
steady_clock::time_point e = steady_clock::now();
//时间点-时间点为时间间隔
steady_clock::duration d = e - s;
std::cout << d.count() << std::endl;
std::system("pause");
}
- 更改了系统时间并不会影响
steady_clock
,所以它是”稳定的“,steady_clock::is_steady
值为true
high_resolution_clock
When available, high_resolution_clock is usually more expensive than the other system-wide clocks, so they are used only when the provided resolution is required to the application.
它是代表了当前系统的所支持的最高精度,是类型别名(可能是system_clock
,也可能是steady_clock
不同平台实现不同),它也只有一个now方法。使用场景跟steady_clock
一致。
三种clock的now的输出
#include <chrono>
#include <iostream>
using namespace std::chrono;
int main()
{
//system_clock
system_clock::time_point st = system_clock::now();
std::cout << st.time_since_epoch().count() << std::endl;
//steay_clock
steady_clock::time_point ste = steady_clock::now();
std::cout << ste.time_since_epoch().count() << std::endl;
//high_resolution_clock
high_resolution_clock::time_point ht = high_resolution_clock::now();
std::cout << ht.time_since_epoch().count() << std::endl;
std::system("pause");
}
system_clock
是系统时间,steady_lock
和high_resolution_clock
是系统运行时间(在vs2017 上 high_resolution_clock就是steady_clock)
三种clock的精度
在每个clock类型都一个period
的成员变量,它是一个ratio类型(比率),以一秒为分子,精度为分母
,可以通过打印period的den成员值来确定clock的精度,如下:
#include <chrono>
#include <iostream>
using namespace std::chrono;
int main()
{
std::cout << system_clock::period::den << std::endl;//输出100000000
std::cout << high_resolution_clock::period::den << std::endl;//输出1000000000
std::cout << steady_clock::period::den << std::endl;//输出1000000000
std::cout << (double)system_clock::period::num / (double)system_clock::period::den << std::endl;
//1e-07
std::cout << (double)high_resolution_clock::period::num / (double)high_resolution_clock::period::den << std::endl;
//1e-09
std::cout << (double)steady_clock::period::num / (double)steady_clock::period::den << std::endl;
//1e-09
std::system("pause");
}
上面的代码是在vs2015上编译测试的结果,system_clock精度为100纳秒,high_resolution_clock精度为1e-09(应该是基于tick),steady_clock精度为1e-09(应该是基于tick)。精度取决于系统的支持,可能在linux下精度会不同了。
三种clock的小结
取系统时间(壁钟时间)应该使用system_clock
,steay_clock
用于度量间隔,high_resolution_clock
使用场景跟steady_clock,因为在实现中它可能就是steady_clock的一个别名。
三种不同类型的clock不同精度的度量间隔,下面的代码如果通过system_clock度量间隔,精度就达不到了
#include <chrono>
#include <iostream>
using namespace std::chrono;
int main()
{
//system_clock::time_point s = system_clock::now();
//high_resolution_clock::time_point s = high_resolution_clock::now();
steady_clock::time_point s = steady_clock::now();
for (int i = 0; i < 10; ++i)
{
}
//system_clock::time_point s1 = system_clock::now();
//high_resolution_clock::time_point s1 = high_resolution_clock::now();
steady_clock::time_point s1 = steady_clock::now();
auto i = s1 - s;
std::cout << i.count() << std::endl;
std::system("pause");
}
Duration
std::ration
在介绍duration前,先要介绍std::ratio
template<
std::intmax_t Num,
std::intmax_t Denom = 1
> class ratio;
std::ratio代表一个比例,Num是分子,Denom是分母, Denom 不可为零且不可等于最负的值。代表的数值约分分子和分母到最简
C++11 标准中的对Duration的定义
template <class Rep, class Period = ratio<1> >
class duration;
A duration object expresses a time span by means of a count and a period.
它代表的是一个时间间隔,Period就是精度(比如秒ration<1>,毫秒ration<1,1000>),一个ratio
类型,ratio以一秒为分子,精度为分母,它支持算术运算符,支持比较运算符
使用基本的时间单元
在chrono
库中,预定义了六种时间单元:hours
,minutes
,seconds
,milliseconds
,microseconds
,nanoseconds
,可以拿来直接使用:
//using seconds = duration<long long>
seconds s(3);//代表3秒钟
seconds s(1);//代表1秒钟
//using minutes = duration<int, ratio<60>>
minutes m1(3); //代表3分钟
minutes h1(60); //代表60分钟
//using milliseconds = duration<long long, milli>;
milliseconds ms(10); //代表10毫秒
milliseconds ms(1000); //代表1000毫秒即1秒
定义有含意的时间单元
在duration中预定义的时,分,秒等,本质上是类型别名,也可以通过这种方式定义在业务系统中有意义的时间单元,比如,在音视频系统,在编码端会设置采集或编码的帧率,比如设置为25帧,可以理解成每40ms产生一帧数据(采集或编码),那么通过duration定义40ms的时间单元
//在25帧的情况下,代表每帧的时间间隔即为40ms(ration<1,25>)
typdef std::chrono::duration<long,std::ratio<1,25>> FrameRate;
//代表一帧的时间间隔
FrameRate(1)
时间点
time_point
表示时间点,在前面提到的公式,将时间点加减一个时间间隔还是一个时间点,可以通过clock将时间点换算成具体时间
template<class Clock,class Duration = typename Clock::duration> class time_point;
一个转换的例子:
#include <chrono>
#include <iostream>
#include <ctime>
using namespace std::chrono;
int main()
{
//定义了一个时间点,代表从system_clock的纪元后的一秒钟
time_point <system_clock, duration<int>> tp_seconds(duration<int>(1));
//将time_point转换为clock
system_clock::time_point tp(tp_seconds);
//格式化输出
std::cout << tp.time_since_epoch().count() << std::endl;
std::time_t tt = system_clock::to_time_t(tp);
std::cout << "time_point tp is: " << ctime(&tt);
std::system("pause");
}
duration_cast
duration_cast可以转化不同类型的时长,比如将分转化成秒
-
在源
duration
能准确地为目标duration
所整除的场合(例如小时到分钟),无需 duration_cast -
浮点时长和整数时长间转型能隐式进行,无需 duration_cast
#include <iostream>
#include <chrono>
#include <ratio>
#include <thread>
void f()
{
std::this_thread::sleep_for(std::chrono::seconds(1));
}
int main()
{
auto t1 = std::chrono::high_resolution_clock::now();
f();
auto t2 = std::chrono::high_resolution_clock::now();
//浮点duration,不需要duration_cast
std::chrono::duration<double,std::milli> f = t2 - t1;
std::cout << f.count() << std::endl;
//整型duration,需要duration_cast
auto int_ms = std::chrono::duration_cast<std::chrono::milliseconds>(t2 - t1);
std::cout << int_ms.count() << std::endl;
//从毫秒到秒,需要duration_cast
std::chrono::seconds int_sec = std::chrono::duration_cast<std::chrono::seconds>(int_ms);
std::cout << int_sec.count() << std::endl;
}
格式化system_clock
的now
方法到微秒
#include <chrono>
#include <iostream>
using namespace std;
using namespace chrono;
int main()
{
//获取当前时间
auto tp = std::chrono::system_clock::now();
//从纪元时间到现在的毫秒数
auto md = std::chrono::duration_cast<std::chrono::milliseconds>(tp.time_since_epoch());
long ms = md.count() % 1000;
//格式化时间
time_t now = std::chrono::system_clock::to_time_t(tp);
tm *info = localtime(&now);
char timefmt[128] = { 0 };
sprintf(timefmt, "%d-%d-%d %d:%d:%d %d", info->tm_year + 1900, info->tm_mon + 1, info->tm_mday, info->tm_hour, info->tm_min, info->tm_sec, ms);
std::cout << timefmt << std::endl;
}