C++11/14 新特性 (使用 chrono 进行时间处理)

在c++11以前,我们获取当前时间的时间戳,需要借助于第三方库,如 boost ,或者针对不同的操作做不同的处理:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
#ifndef _MSC_VER
#include <sys/time.h>
#endif

long long GetTimeSpan()
{
long long llTimeMs = 0L;
#ifdef _MSC_VER
timeb tb;
ftime(&tb);

lTimeMs = tb.time * 1000 + tb.millitm;

#else
timeval tmv;
gettimeofday(&tmv,NULL);

llTimeMs = (long long)(tmv.tv_sec) * 1000L + tmv.tv_usec / 1000;

#endif
return llTimeMs;
}

而在c++11中,这个问题得到解决。 c++11 标准库中提供 chrono库,用来处理时间相关的数据。

1 duration 记录时长

duration 表示一段时间间隔。其原型:

1
2
template <class _Rep, class _Period>
class duration;

_Rep 为一个数值类型,如 int, long, 用来表示时钟数的类型。 _Period 为一个默认的模板参数 std::ratio,表示时钟周期。 ratio的原型:

1
2
template <intmax_t _Num, intmax_t _Den = 1>
class ratio

它表示一个时钟周期的秒数。_Num 代表分子,_Den代表分母,它们的比值就是一个时钟周期,可以通过调整分子与分母来表示任意一个时钟周期。如 ratio<1> 表示一个时钟周期为1秒,ratio<3600>表示一个时钟周期为1小时,ratio<1,1000>表示一个时钟周期为1毫秒,ratio<1,2>表示一个时钟周期为0.2秒,ratio<9/7>表示一个时钟周期为9/7秒。 标准库将一些常用的时钟周期做了定义:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
typedef ratio<1LL, 1000000000000000000LL> atto;
typedef ratio<1LL, 1000000000000000LL> femto;
typedef ratio<1LL, 1000000000000LL> pico;
typedef ratio<1LL, 1000000000LL> nano;
typedef ratio<1LL, 1000000LL> micro;
typedef ratio<1LL, 1000LL> milli;
typedef ratio<1LL, 100LL> centi;
typedef ratio<1LL, 10LL> deci;
typedef ratio< 10LL, 1LL> deca;
typedef ratio< 100LL, 1LL> hecto;
typedef ratio< 1000LL, 1LL> kilo;
typedef ratio< 1000000LL, 1LL> mega;
typedef ratio< 1000000000LL, 1LL> giga;
typedef ratio< 1000000000000LL, 1LL> tera;
typedef ratio< 1000000000000000LL, 1LL> peta;
typedef ratio<1000000000000000000LL, 1LL> exa;

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< long, ratio< 60> > minutes;
typedef duration< long, ratio<3600> > hours;

在不同的时钟周期中,我们可以使用 chrono_cast 来进行转换。两个 duration还可以进行加减运算:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
#include <chrono>
#include <iostream>
using namespace std;

int main(int argc, const char * argv[]) {

chrono::hours hrs(2); //两小时
cout<<hrs.count()<<" hours = "; //count() 函数获取时钟周期下的时钟数
chrono::minutes mns = chrono::duration_cast<chrono::minutes>(hrs); //120分钟
cout<<mns.count()<<" minutes"<<endl;

chrono::minutes tm = hrs - chrono::minutes(30); //时长的加减运算
cout<<hrs.count()<<" hours - 30 minutes = " <<tm.count()<<" minutes = ";

typedef chrono::duration<double,ratio<3600,1>> hs; //时长的类型转换。注意 duration 的 _Rep 参数类型。
hs c = chrono::duration_cast<hs>(tm);
cout<<c.count()<<" hours"<<endl;

return 0;
}

//# 输出 #:
// 2 hours = 120 minutes
// 2 hours - 30 minutes = 90 minutes = 1.5 hours

2 clocks 系统时钟

它表示当前的系统时钟:

  • system_clock : 真实时钟,它依赖于系统时间。

  • steady_clock : 稳定时钟。它不依赖于系统时间,不会因为系统时间的调整而变化。

  • high_resolution_clock : 高精度时钟。它实际上是 system_clock 或 stead_clock 的高精度版本。

它可以实现 time_t 到 time_point (见下一小节)的相互转换。

3 time_point 时间点(时间戳)

它与表示距离某个时间原点的时钟数。其原型如下:

1
2
template <class _Clock, class _Duration = typename _Clock::duration>
class time_point

表示距离 _Clock 有多少个 _Duration。

time_point 提供 time_point_eproch() 函数来获取从 1970年1月1日到time_point 经过的 duration. time_point 还支持加减算术运算。如果看其源码,可以发现其加减运算就是拿 duration来进行运算,故其 time_point 还可以直接和 duration 进行运算。time_point 的 ‘-’ 的某个版本的重载如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
template <class _Clock, class _Duration1, class _Rep2, class _Period2>
inline _LIBCPP_INLINE_VISIBILITY _LIBCPP_CONSTEXPR_AFTER_CXX11
time_point<_Clock, typename common_type<_Duration1, duration<_Rep2, _Period2> >::type>
operator-(const time_point<_Clock, _Duration1>& __lhs, const duration<_Rep2, _Period2>& __rhs)
{
return __lhs + (-__rhs);
}

// duration operator-(time_point x, time_point y);

template <class _Clock, class _Duration1, class _Duration2>
inline _LIBCPP_INLINE_VISIBILITY _LIBCPP_CONSTEXPR_AFTER_CXX11
typename common_type<_Duration1, _Duration2>::type
operator-(const time_point<_Clock, _Duration1>& __lhs, const time_point<_Clock, _Duration2>& __rhs)
{
return __lhs.time_since_epoch() - __rhs.time_since_epoch();
}

示例:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
#include <chrono>
#include <iostream>
#include <unistd.h> //for sleep()
#include <sys/time.h> //for time_t/ctime
using namespace std;

int main(int argc, const char * argv[]) {

using namespace chrono;
time_point<system_clock> t1 = system_clock::now();
time_point<high_resolution_clock> ht1 = high_resolution_clock::now();
sleep(3);
time_point<system_clock> t2 = system_clock::now();
time_point<high_resolution_clock> ht2 = high_resolution_clock::now();
auto t = t2 - t1;
auto ht = ht2 - ht1;

time_t tmt1 = system_clock::to_time_t(t1);
time_t tmt2 = system_clock::to_time_t(t2);
cout<<"当前时间为:"<< ctime(&tmt1)<<endl;
cout<<"经过了 3 秒 : "
<<t.count() <<"\t" //
<<ht.count() //高精度时钟数
<<endl;
cout<<"现在时间为:"<< ctime(&tmt2)<<endl;

return 0;
}

//# 输出 #:
//当前时间为:Sun Jan 1 15:12:32 2017
//经过了 3 秒 : 3005337 3005156648
//现在时间为:Sun Jan 1 15:12:35 2017