使用chrono获取时间

chrono自C++11以后加入了标准库,提供了精度和时间计算的接口。其中包含三种时钟system_clocksteady_clockhigh_resolution_clock,顾名思义,system_clock是系统时钟,始终和系统的时间保持一致,因此在使用的过程中如果系统时间被修改了,那么使用了这个时钟的程序就会被影响。steady_clock可以解决运行过程中系统时间被修改而被影响的问题,是一种稳定的单调时钟,所以更适合于度量时间间隔的场景。high_resolution_clock定义上是一种高精度的时钟,其精度取决于系统环境可以达到的最小精度(不过我在windows平台上使用13.1版本的Mingw-w64测试了一下,cpu是13600kf,能显示的时间精度和系统时间是一样的,都是纳秒级别,也没体现出高精度的特点,可能是纳秒级精度已经比较高了的原因),使用到的场景似乎不多,前面两个已经够用了。

查看各个时钟的精度

1
2
3
4
5
6
cout << "system_clock precision: ";
cout << chrono::system_clock::period::num << "/" << chrono::system_clock::period::den << endl;
cout << "steady_clock precision: ";
cout << chrono::steady_clock::period::num << "/" << chrono::steady_clock::period::den << endl;
cout << "chrono::high_resolution_clock precision: ";
cout << chrono::high_resolution_clock::period::num << "/" << chrono::high_resolution_clock::period::den << endl;

输出结果

1
2
3
system_clock precision:                     1/1000000000
steady_clock precision: 1/1000000000
chrono::high_resolution_clock precision: 1/1000000000

公有静态成员函数 now

三种时钟都有公有的静态成员函数now(),返回当前的时间点,记录了从某一时刻到当前时间的时钟周期数。

1
2
3
4
5
6
chrono::time_point<chrono::steady_clock, chrono::nanoseconds> curTime = chrono::steady_clock::now();        
cout << "time_since_epoch (steady_clock): \t\t" << curTime.time_since_epoch().count() << endl;
chrono::time_point high_curTime = chrono::high_resolution_clock::now();
cout << "time_since_epoch (high_resolution_clock): \t" << high_curTime.time_since_epoch().count() << endl;
chrono::time_point sys_curTime = chrono::system_clock::now();
cout << "time_since_epoch (system_clock): \t\t" << sys_curTime.time_since_epoch().count() << endl;

输出结果:

1
2
3
time_since_epoch (steady_clock):                117212202374400
time_since_epoch (high_resolution_clock): 1710767518931123200
time_since_epoch (system_clock): 1710767518931709300

系统时间返回的是世界协调时(Unix时间),从1970年1月1日00:00:00开始的时间,不计闰秒(从C++20起是规定,之前的标准没有明确指定,不过大多也是这么实现的)。高精度时钟也取了系统时间,不过这不是一定的,因为没有明确的标准指定高精度时钟必须这么做。而稳定时钟计算了从某刻开始到当前的时钟周期。

和C时间的转换

系统时钟可以和C标准库中的时间结构进行转换,例如

1
2
3
time_t time_c = chrono::system_clock::to_time_t(sys_curTime);                                                
cout << "system_clock::to_time_t: \t\t\t" << time_c << endl;
cout << "ctime: \t\t\t\t\t\t" << ctime(&time_c) << endl;

输出结果:

1
2
system_clock::to_time_t:                        1710767518
ctime: Mon Mar 18 21:11:58 2024

顺便看一下C库中获取时间的结构

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
time_t now_c = time(NULL);      // 返回从1900至今的秒数,入参不为NULL则也赋给time_t类型的入参
cout << "now: " << now_c << endl;
char *date = ctime(&now_c);
cout << "ctime(&now): " << date << endl; // 将时间戳转换成字符串

// 转换utc时间
tm *utcTime = gmtime(&now_c);
cout << "struct {\n" << " tm_year: " << utcTime->tm_year << " (since 1900)" << endl;
cout << " tm_month: " << utcTime->tm_mon << " (since 0)" << endl;
cout << " tm_yday: " << utcTime->tm_yday << endl;
cout << " tm_mday: " << utcTime->tm_mday << endl;
cout << " tm_wday: " << utcTime->tm_wday << endl;
cout << " tm_hour: " << utcTime->tm_hour << endl;
cout << " tm_minute: " << utcTime->tm_min << endl;
cout << " tm_second: " << utcTime->tm_sec << endl;
cout << "}tm;\n";
// 转成字符串输出
date = asctime(utcTime);
cout << date << endl;

输出:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
now: 1710767518
ctime(&now): Mon Mar 18 21:11:58 2024

struct {
tm_year: 124 (since 1900)
tm_month: 2 (since 0)
tm_yday: 77
tm_mday: 18
tm_wday: 1
tm_hour: 13
tm_minute: 11
tm_second: 58
}tm;
Mon Mar 18 13:39:03 2024

需要引入ctime库,使用time(NULL)返还了从1900年至今的秒数,C中的时间结构精度只能到秒,所以需要更高精度的话,还是要去使用chrono库。
不过ctime库中的tm结构可以表示年月日时分秒,倒是挺方便。

时间运算

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
chrono::hours hour(1);
chrono::minutes minute(1);
auto diff = hour - minute; //得到的结果单位取小
cout << "1 hour - 1 minute = " << diff.count() << endl;

chrono::steady_clock::time_point steadyTimePoint;
cout << "curTimePoint - defaultTimePoint = " << chrono::duration_cast<chrono::nanoseconds>(curTime - steadyTimePoint).count() << endl; // 时间点和时间点相减得到时间段
// auto addTimePoint = curTime + steadyTimePoint; // 时间点之间不能相加
auto nextTimePoint = curTime + chrono::minutes{1}; // 时间点可以加时间段,得到下个时间点
cout << "当前时间点加一分钟: " << chrono::duration_cast<chrono::seconds>(nextTimePoint - steadyTimePoint).count() << endl;
// 时间段之间允许算数运算
chrono::minutes fourMinutes(4);
chrono::seconds twoSeconds(2);
cout << "4minutes + 2second = " << (fourMinutes + twoSeconds).count() << endl;
cout << "4minutes - 2second = " << (fourMinutes - twoSeconds).count() << endl;
// cout << "4minutes * 2second = " << (fourMinutes * twoSeconds).count() << endl; // 不允许时间相乘,没有意义
cout << "4minutes * 2 = " << (fourMinutes * 2).count() << endl;
cout << "4minutes / 2second = " << (fourMinutes / twoSeconds) << endl; // 时间段相除得到的是个值
chrono::seconds sevenSeconds(7);
cout << "4minutes % 7second = "s << (fourMinutes % sevenSeconds).count() << endl;

cout << "-----------------------------------------------------------------------------\n";
// 自定义时间段,第一个模板参数是数据类型,整数或者小数,第二个参数为比率
chrono::duration<int, ratio<1, 16>> sevenOfSixteen(7);
chrono::duration<int, ratio<1, 8>> OneOfEight(1);
cout << "7/16 - 3/8 = " << (sevenOfSixteen - OneOfEight).count() << endl; // 结果取小的单位 得到5个1/16
cout << "两个自定义的时间段不是倍数关系的情况下:" << endl;
chrono::duration<double, ratio<1, 11>> nineOfEleven(9.0);
chrono::duration<double, ratio<1, 7>> fiveOfSeven(5.0);
cout << "5.0/7 - 9.0/11 = " << (fiveOfSeven - nineOfEleven).count() << endl; // res = -8, 结果是取公倍数为分母的结果,也就是-8个1/77

输出结果:

1
2
3
4
5
6
7
8
9
10
11
12
1 hour - 1 minute = 59
curTimePoint - defaultTimePoint = 118836333058000
当前时间点加一分钟: 118896
4minutes + 2second = 242
4minutes - 2second = 238
4minutes * 2 = 8
4minutes / 2second = 120
4minutes % 7second = 2
-----------------------------------------------------------------------------
7/16 - 3/8 = 5
两个自定义的时间段不是倍数关系的情况下:
5.0/7 - 9.0/11 = -8

chrono中时间的计算是通过ratio<>来实现的,可以看到,1小时减去1分钟后,精度会取较小的单位,结果等于59。
chrono内有时间点和时间段的概念,两个时间点相减可以得到两者相差的时间段,一个时间点可以加上一个时间段获取某个新的时间点,而两个时间点之间是不能相加的,这也符合日常生活中的印象。
我们还可以使用ratio<>模板自定义一个精度,并且能够进行分数的计算,正如输出中显示的那样。