本文根据众多互联网博客内容整理后形成,引用内容的版权归原始作者所有,仅限于学习研究使用,不得用于任何商业用途。
这篇算是简单介绍一下,C++11 的标准函式库里面,用来处理时间和日期的函式库「chrono」。这个函数库是在 C++11 才引进的,以微软的 Visual C++ 来说,要到 2012 版才开始提供;而如果使用的开发环境没有支援的话,也可以使用 Boost C++ Libraries 所提供的版本。
Chrono 这个函数库,主要是为 C++ STL 加入一个可以取得、处理时间与日期的函数库;透过这个函数库,我们可以在程序里面取得时间、并针对时间做处理、计算。而像是 STL Thread 的 sleep_for() 或 sleep_until(),也都是透过 chrono 提供的型别来做设定。
要使用这个函式库,要 include 他的 header 档,也就是加上
#include <chrono>
之后用的东西,基本上都是在 std:chrono 这个 namespace 下。
使用 chrono 时,最主要应该是下面这两种用来记录时间的类别:
- 纪录时间点的 time_point
- 纪录时间长度的 duration
duration
duration 是 chrono 裡面,用来记录时间长度的类别,他基本上是一个 template class,可以自行定义他的意义;chrono 也有提供一些比较常见的时间类别,可以直接拿来使用,下面就是内建的 duration 的型别:
typedef duration<long long, nano> nanoseconds;
typedef duration<long long, micro> microseconds;
typedef duration<long long, milli> milliseconds;
typedef duration<long long> seconds;
typedef duration<int, ratio<60> > minutes;
typedef duration<int, ratio<3600> > hours;
其中可以看到,第一个 template 参数是要用来储存资料的类型,第二个则是他相对于「秒」的比例。这边也使用了 ratio 这个 C++11 的另一个新的函式库的类别,他是用来记录「有理数」(可以写成分数的数)的新类别,有兴趣可以参考 cppreference 的介绍。
基本上,一般会用到时间单位这边都有定义好了,如果不合用的话,也可以自己去定义;而由于 chrono 也有把相关的计算都定义了,所以也可以直接拿来做计算,就算是时间单位不同,也不会有问题。
下面就是一个简单的例子:
std::chrono::minutes t1( 10 );
std::chrono::seconds t2( 60 );
std::chrono::seconds t3 = t1 - t2;
std::cout << t3.count() << " second" << std::endl;
其中,t1 是代表 10 分钟、 t2 是代表 60 秒,t3 则是 t1 减去 t2,也就是 600 – 60 = 540 秒。
而如果要取得一个 duration 的值的话,则是要呼叫他的 count() 这个函式;像在上面的例子裡面,就会把 t3 的值输出,所以最后会出现「540 second」。
而如果想要做强制的时间单位转换,也可以使用 duration_cast<>() 这个函式来做;下面就是一个把以秒为单位的 t3 转换成分钟后再输出。
cout << chrono::duration_cast<chrono::minutes>( t3 ).count() << endl;
time_point
相较于 duration 是用来记录时间的长度的,time_point 是用来记录一个特定时间点的资料类别。他一样是一个 template class,需要指定要使用的 clock 与时间单位(duration)。
Chrono 一般来说有提供两种 clock 可以使用,分别是:system_clock 和 steady_clock。
其中 system_clock 是直接去抓系统的时间,有可能在使用中会被被修改(参考);
而 steady_clock 则是确实地去纪录时间的流逝,所以不会出现时间倒退的状况(参考)。
一般要使用的话,大概会是下面的样子:
std::chrono::steady_clock::time_point t1 = std::chrono::steady_clock::now();
std::cout << "Hello World\n";
std::chrono::steady_clock::time_point t2 = std::chrono::steady_clock::now();
std::cout << "Printing took "
<< std::chrono::duration_cast<std::chrono::microseconds>(t2 - t1).count()
<< "us.\n";
通过 clock 类别所提供的 now() 这个函数,可以快速地取得现在的时间;而两者相减的话,则会产生一个型别为 duration 的结果;在上面的例子裡面,就是一开始先取得当下的时间 t1,然后输出一个字串后、再去取得一个时间 t2,之后两者相减,就可以取得中间过程所花费的时间了。在这边则是在相减后,把结果转换成以 micro second 为单位后,再做输出。
而 time_point 也可以和 duration 做计算,得出新的 time_point;例如下面的程式码,就是计算 10 个小时候的时间:
std::chrono::system_clock::time_point now = std::chrono::system_clock::now();
std::chrono::system_clock::time_point nt = now + std::chrono::hours(10);
另外,chrono 也有定义 high_resolution_clock,提供更高的精确度(参考);但是实际上在 MSVC11 上,他就等同于 system_clock。
而如果要把不同定义的 time_point 做转换,则也可以使用 time_point_cast<>() 这个函式来处理,不过这边就不多加说明了。
time_point 的输出
STL 的 chrono 并没有定义 time_point 的输出方式,所以我们并不能直接透过 output stream 来输出 time_point 的资料,所以如果要把他输出成字串的话,其实还有点麻烦…
如果想要输出的话,一个方法是透过 clock 提供的 to_time_t() 这个函式,把 time_point 先把他转换成 C-style 的 time_t,然后再透过 ctime() 这类的函式做输出;下面是一个简单的范例:
std::chrono::system_clock::time_point now = std::chrono::system_clock::now();
std::time_t now_c = std::chrono::system_clock::to_time_t( now );
std::cout << std::ctime( &now_c ) << std::endl;
而如果是使用 Boost 的版本的话,Boost 则是另外有提供 chrono_io.hpp 这个档桉,在裡面替 duration 和 time_point 定义了输出的格式,可以直接使用,相当地方便~有兴趣的话,可以参考 Boost 的官方说明。
参考资料:
C++11 STL 的時間函式庫:chrono
http://en.cppreference.com/w/cpp/header/chrono
http://msdn.microsoft.com/en-us/library/hh874757.aspx
http://www.boost.org/doc/libs/1_55_0/doc/html/chrono.html