技术: C++编程之time问题总结

习惯用的使用c-style的时间, 导致有时候在cpp程序中看着很别扭.

网上的有些文章看的实在是太敷衍, 索性自己写一篇吧.

引子

本文以如下方式展开:

  • 时间相关的概念
  • Linux C中常用时间编程方式和手段
  • 现代C++时间编程的方式和惯例

(代码, 命令环境Ubuntu 14)

正文

相关概念

UTC

Coordinated Universal Time, 即GMT(Greenwich Mean Time); 把格林威治标准时间作为标准时间.
比如, 中国内地的时间与UTC的时差为+8, 也就是UTC+8, 美国是UTC-5.

CST

CST: 中部标准时间, 这个时间是要分时区的. 一般看到的是:

  • CST China Standard Time
  • CST Cuba Standard Time
1
2
$ date
Tue Jul 29 08:07:51 CST 2017

DST

Daylight Saving Time, 又称“日光节约时制”和“夏令时间”,是一种为节约能源而人为规定地方时间的制度,在这一制度实行期间所采用的统一时间称为“夏令时间”。一般在天亮早的夏季人为将时间调快一小时,可以使人早起早睡,减少照明量,以充分利用光照资源,从而节约照明用电。

根据时区不同, 也不有不同算法调整, 具体可以参考man手册 man gettimeofday .

1
2
3
4
5
6
7
#include <sys/time.h>
int gettimeofday(struct timeval *tv, struct timezone *tz); //第二个参数一般传入NULL

struct timezone {
int tz_minuteswest; /* minutes west of Greenwich */
int tz_dsttime; /* type of DST correction */
};

Epoch

1970年1月1日0:0:0 UTC(世界标准时间)
相当于计时起始时间.

CalendarTime

日历时间 是用 “从一个标准时间点到此时的时间经过的秒数” 来表示的时间, 它是一个相对时间. (距离时间起点经过的时间)

这个标准时间点对不同的编译器来说会有所不同, 但对一个编译系统来说, 这个标准时间点是不变的, 该编译系统中的时间对应的日历时间都通过该标准时间点来衡量, 所以可以说日历时间是”相对时间”, 但是无论你在哪一个时区, 在同一时刻对同一个标准时间点来说, 日历时间都是一样的.

下面的命令可以查看自1970年1月1日0:0:0 UTC(世界标准时间)到现在的秒数:

1
$ date +%s

然后把这些时间按照我们的年月日换算就可以算出现在的日历时间:

1
$ date -d @秒数

ClockTick

时钟计时单元(而不把它叫做时钟滴答次数), 一个 clock tick 不是 CPU 的一个时钟周期, 而是系统的一个基本计时单位(时钟中断的间隔时长).

Second

时间单位:

  • millisecond 毫秒 ms 1e-3秒
  • microsecond 微秒 us 1e-6秒
  • nanosecond 纳秒 ns 1e-9秒
  • picosecond 皮秒 ps 1e-12秒

我一般记忆到纳秒:

1秒是1000毫秒, 100万微妙, 10亿纳秒.


下面总结编程中需要用的时间内容, 一般是为了

  • 具体某个函数调用
  • 统计进程运行时间
  • 获取系统时间
  • profile/性能测试

C部分

在Linux C下有两个重要的头文件, time.h, sys/time.h, 主要还是使用 time.h.

结构体定义

常用的结构体或定义:

  • time_t 统计秒数(注意范围)
  • clock_t 时钟单位值(一般用于统计程序运行时间)
  • timeval 时间间隔(一般用于gettimeofday, 定时器之类函数参数上)
  • tm 正规时间格式, 分解时间(broken-down time)

你不要关心具体的实现或者定义, 但是知道最好(可能你需要在time_t和tm之间进行转换).

下面说一下: (环境Ubuntu 64)

  • time_t的定义:

    1
    2
    3
    4
    5
    6
    __STD_TYPE __TIME_T_TYPE __time_t;      /* Seconds since the Epoch.  */

    #define __TIME_T_TYPE __SLONGWORD_TYPE
    #define __SLONGWORD_TYPE long int

    //微妙suseconds_t 也是long int

    (他和tm结构体完全不一样)一看就是记录的当前时间秒数, 但是各个编译器厂商记录该时间的定义可能不太一样, 在Visual C++中采用了__time64_t数据类型来保存日历时间, 相应的通过 _time64()来获取日历秒数(这样表示的大小更大了)

  • timeval的定义: (man gettimeofday)

    1
    2
    3
    4
    5
    6
    7
    8
    9
    struct timeval {
    time_t tv_sec; /* seconds */
    suseconds_t tv_usec; /* microseconds */
    };

    struct timezone {
    int tz_minuteswest; /* minutes west of Greenwich */
    int tz_dsttime; /* type of DST correction */
    };

    如果你使用则要手动加宏:

    1
    2
    #include <time.h>
    #define __need_timeval 1

    不加宏, 直接用

    1
    # include <sys/time.h>

    小的测试:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    #include <stdio.h>
    #include <stdlib.h>
    #include <sys/time.h>
    #include <time.h> //ctime


    int main(void)
    {
    struct timeval tv;
    gettimeofday(&tv, NULL);

    printf("%s \n", ctime(&tv.tv_sec) );

    return 0;
    }
  • tm 的定义

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    struct tm {
    int tm_sec; /* seconds */
    int tm_min; /* minutes */
    int tm_hour; /* hours */
    int tm_mday; /* day of the month */
    int tm_mon; /* month */
    int tm_year; /* year */
    int tm_wday; /* day of the week */
    int tm_yday; /* day in the year */
    int tm_isdst; /* daylight saving time */
    };

    值得注意的是, glic可能有自己的实现, 也就是说还有更多的字段. 其他详细信息可以参考 man ctime 里的说明.

  • clock_t 定义:

    1
    2
    3
    4
    #ifndef _CLOCK_T_DEFINED
    typedef long clock_t;
    #define _CLOCK_T_DEFINED
    #endif

    还定义了一秒有多少个 时钟单元 :

    1
    #define CLOCKS_PER_SEC ((clock_t)1000)

函数定义

定义在 time.h 中的重要函数:

  • extern clock_t clock(time_t *__timer); //查看进程运行时间
  • extern time_t time(time_t *__timer); //获取当前系统时间(你拿到的是秒数)
  • extern char ctime(__const time_t __timer); //把秒数转换成当前时间格式字符串
  • extern char asctime(const struct tm tm); //作用同上, 参数不同
  • extern struct tm gmtime(__const time_t __timer);//作用同上, 不过返回是UCT格式结构体
  • extern struct tm localtime(const time_t __timer); //作用同上, 不过是当前时区格式结构体
  • extern time_t mktime(struct tm *__time); //得到当前时间秒数
  • extern double difftime(time_t time1, time_t time0); //计算时间差, 可以自己算

还有一个提取 struct tm 时间结构体某一项的函数:(类似 sprintf 这种格式化输出)

1
2
3
4
5
6
//特别注意, 这个函数返回的 buf 没有\0结尾.
//并且在存储相关内容之前, 长度不够, 那么返回值为0, 且buf未定义.
extern size_t strftime(char *buf,
size_t max,
const char *format,
const struct tm *tm);

format 的取值如下:

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
34
35
36
37
38
/*
%a 星期几的简写
%A 星期几的全称
%b 月分的简写
%B 月份的全称
%c 标准的日期的时间串
%C 年份的后两位数字
%d 十进制表示的每月的第几天
%D 月/天/年
%e 在两字符域中,十进制表示的每月的第几天
%F 年-月-日
%g 年份的后两位数字,使用基于周的年
%G 年分,使用基于周的年
%h 简写的月份名
%H 24小时制的小时
%I 12小时制的小时
%j 十进制表示的每年的第几天
%m 十进制表示的月份
%M 十时制表示的分钟数
%n 新行符
%p 本地的AM或PM的等价显示
%r 12小时的时间
%R 显示小时和分钟:hh:mm
%S 十进制的秒数
%t 水平制表符
%T 显示时分秒:hh:mm:ss
%u 每周的第几天,星期一为第一天 (值从0到6,星期一为0)
%U 第年的第几周,把星期日做为第一天(值从0到53)
%V 每年的第几周,使用基于周的年
%w 十进制表示的星期几(值从0到6,星期天为0)
%W 每年的第几周,把星期一做为第一天(值从0到53)
%x 标准的日期串
%X 标准的时间串
%y 不带世纪的十进制年份(值从0到99)
%Y 带世纪部分的十制年份
%z %Z 时区名称,如果不能得到时区名称则返回空字符。
%% 百分号
*/

一个小demo: (12进制显示当前时间)

1
2
3
4
5
6
7
char str[100] = {0};
//memset(str, 0, 100);
struct tm *tm_ptr = localtime(time(NULL));
strftime(str, 100, "It is now : %I %p", pt);
str[strlen(str)] = '\0';
//输出str
printf("%s\n", str);

注意:

  • 注意 clock() 函数, 返回的是 usr time + system time, 即用户时间+内核时间, 返回的时间(始终计数器值)需要转换成秒, 可以用下面的公式:

    1
    2
    3
    4
    5
    6
    //result / CLOCKS_PER_SECOND; //1毫秒, 时钟计数器+1, 即调用clock()函数返回的值就加1

    void elapsed_time()
    {
    printf("Elapsed time:%u secs./n", clock()/CLOCKS_PER_SEC);
    }

    当然你可以计算某一个调用, 某一个循环化了多长时间:(但是精度到毫秒)

    1
    2
    3
    4
    5
    6
    7
    long    i = 10000000L; //把i减至0花了多长时间

    clock_t start = clock();
    while( i-- );
    clock_t finish = clock();

    double duration = (double)(finish - start) / CLOCKS_PER_SEC;
  • asctimectime 将时间以固定的格式显示出来,两者的返回值都是char*型的字符串, 但是参数不同

  • 注意 gmtime() 以及 localtime 是可以把 time_t 转换成 tm 的; 这两个函数涉及到了tm, 也就不仅仅只有时间, 还有日期了
  • 注意 mktime() 可以把 tm 当前时间格式, 转换成 time_t 当前时间秒数
  • 注意 time 拿到的是当前时间的秒数, 见到某平台的库实现 _time64() 也不必惊慌

完整的定义可以查看 man 手册:

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

char *asctime(const struct tm *tm);
char *asctime_r(const struct tm *tm, char *buf);

char *ctime(const time_t *timep);
char *ctime_r(const time_t *timep, char *buf);

struct tm *gmtime(const time_t *timep);
struct tm *gmtime_r(const time_t *timep, struct tm *result);

struct tm *localtime(const time_t *timep);
struct tm *localtime_r(const time_t *timep, struct tm *result);

time_t mktime(struct tm *tm);

/*--------------------------------------------------------------*/
#include <time.h>

size_t strftime(char *s, size_t max, const char *format,
const struct tm *tm);

定义在sys/time.h中的重要函数

1
2
3
4
#include <sys/time.h>

int gettimeofday(struct timeval *tv, struct timezone *tz);
int settimeofday(const struct timeval *tv, const struct timezone *tz);

综合案例

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
34
35
#include <stdio.h>
#include <stdlib.h>
#include <time.h>
#include <string.h> //memset


int main(void)
{
/*
time_t timep;
time_t *timep_ptr = (time_t*)malloc(sizeof(time_t));
timep = time(timep_ptr); //读取时间, 秒数
printf("ctime returns %s\n", ctime(&timep)); //转换成字符串输出
printf("ctime returns %s\n", ctime(timep_ptr));
*/

//推荐
time_t timep;
time(&timep);
printf("ctime returns %s\n", ctime(&timep)); //转换成字符串输出

printf("local time: %s\n", asctime(localtime(&timep)) );
printf("gmt time: %s\n", asctime(gmtime(&timep)) );

//查看时区
char buf[128];
memset(buf, 0, 128);

printf("globe time: ");
//strftime(buf, 128, "%Z", gmtime(&timep)); //获取时区信息
strftime(buf, 128, "%Z", localtime(&timep)); //获取时区信息
printf("%s\n", buf);

return 0;
}

Cpp部分

已经看到了, 如果你去使用C库 time.h, 那么时间操作都是够用的; 但是在C++里一般用 ctime, 如下图:

其实是没有差距的, 如果你要用 ctime 这个头文件的话(注意, 加上命名空间 std:: ).

但是C++不仅仅包含 C-style date and time library , 这里还从Boost库里引入了 chrono 库:

The chrono library, a flexible collection of types that track time with varying degrees of precision (e.g. std::chrono::time_point)

chrono库包含3个内容:

  • durations (std::chrono::duration类模板, 就相当于一个自带时间单位的时间间隔)
  • clocks (three clock types)
    • system_clock类 (该类的is_steady属性为false, 会随着系统时间而改变)
    • steady_clock类 (不会因为你修改系统时间而改变)
    • high_resolution_clock类 (一般是上面两种的宏定义, 具体看平台)
  • time points (time_point类模板)–时间点

其中比较有用的就是 system_clock, 相当于原来clock()函数扩展成了一个类, 和 time_point 模板类(可以以system_lock时钟为计时单位参数), 以及计算时间价格的方法的封装类模板 duration. (其实你可以看到, 都是原来时间函数的封装, 以面向对象的姿态展现)

使用起来, 真的是太方便了, 下面给出一个简单的综合案例:
(统计函数调用时间的, 和我们上面c库中clock用法类似)

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
34
35
36
37
38
39
#include <iostream>
#include <chrono>
#include <ctime>

long fibonacci(unsigned n)
{
if (n < 2) return n;
return fibonacci(n-1) + fibonacci(n-2);
}

int main()
{
/*
//原来的写法
clock_t start = clock();
while( i-- );
clock_t finish = clock();
double duration = (double)(finish - start) / CLOCKS_PER_SEC;
*/

//using namespace std::chrono;

//以system_clock为计时类型的时间点
//time_point<system_clock> start, end;
std::chrono::time_point<std::chrono::system_clock> start, end;

//start = system_clock::now();
start = std::chrono::system_clock::now();
std::cout << "f(42) = " << fibonacci(42) << std::endl;
end = std::chrono::system_clock::now();

std::chrono::duration<double> elapsed_seconds = end-start;

//time_point 转换成 time_t
std::time_t end_time = std::chrono::system_clock::to_time_t(end);

std::cout << "finished computation at " << std::ctime(&end_time)
<< "elapsed time: " << elapsed_seconds.count() << "s\n";
}

下面一个个仔细说:


duration

  
std::chrono::duration 表示一段时间间隔, 用来记录时间长度(这个时候你可以在模板参数里指定计时单位, 默认是秒), 其原型如下:

1
template<class Rep, class Period = std::ratio<1>> class duration;//默认秒做时间单位

  • 第一个模板参数 Rep 是一个数值类型(一般是int, 多少秒, 分等),表示时钟个数;
  • 第二个模板参数是一个默认模板参数std::ratio,它的原型是:

    1
    template<std::intmax_t Num, std::intmax_t Denom = 1> class ratio;

    它表示每个时钟周期的秒数, 其中第一个模板参数 Num 代表分子, Denom代表分母, 分母默认为1, ratio代表的是一个分子除以分母的分数值, 比如 ratio<2> 代表一个时钟周期是两秒, ratio<60>代表了一分钟, ratio<60*60>代表一个小时, ratio<60*60*24>代表一天. 而ratio<1, 1000="">代表的则是1/1000秒即一毫秒, ratio<1, 1000000="">代表一微秒, ratio<1, 1000000000="">代表一纳秒.
    标准库为了方便使用, 就定义了一些常用的时间间隔, 如时、分、秒、毫秒、微秒和纳秒, 在chrono命名空间下, 它们的定义如下:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    //在windows平台, 这里的Rep是 整型(不一定是int,可能是int64)
    typedef duration<Rep, ratio<3600,1>> hours; //std::ratio<3600>
    typedef duration<Rep, ratio<60,1>> minutes; //std::ratio<60>
    typedef duration<Rep, ratio<1,1>> seconds; //std::ratio<1>
    typedef duration<Rep, ratio<1,1000>> milliseconds;
    typedef duration<Rep, ratio<1,1000000>> microseconds;
    typedef duration<Rep, ratio<1,1000000000>> nanoseconds;

    //当然你也可以完全自己定义, 比如说我定义半分钟
    typedef duration<long, ratio<30>> halfminutes;

    之后你直接使用这些定义好的单位即可(不用再直接使用duration), 例如:

    1
    2
    3
    //线程睡眠
    std::this_thread::sleep_for(std::chrono::seconds(3)); //休眠3秒
    std::this_thread::sleep_for(std::chrono::milliseconds (100)); //休眠100毫秒

    但是注意和 std:: 名字空间区分, 例如 std::nano; std::chrono:: 下一般是全拼写.

其他一些重要的方法:

  • count()

    1
    constexpr rep count() const;//获取时钟(单位)个数, 与单位无关

    例如:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    #include <chrono>
    #include <iostream>
    int main(void)
    {
    std::chrono::milliseconds ms{3}; // 3 milliseconds
    // 6000 microseconds constructed from 3 milliseconds
    std::chrono::microseconds us = 2*ms;
    // 30Hz clock using fractional ticks
    std::chrono::duration<double, std::ratio<1, 30>> hz30(3.5);

    std::cout << "3 ms duration has " << ms.count() << " ticks\n"
    << "6000 us duration has " << us.count() << " ticks\n"
    << "3.5 30Hz duration has " << hz30.count() << " ticks\n";
    }

    /* output:
    3 ms duration has 3 ticks //count = 3
    6000 us duration has 6000 ticks //count =6000
    3.5 30Hz duration has 3.5 ticks //cout = 3.5
    */

    并且注意上面的赋值 us = 2*ms , 大单位毫秒赋值给微妙是可以的, 但是反过来产生小数就不行了, 这个时候需要自己定义了:

    1
    2
    3
    4
    typedef duration<double, ratio<1,1000>> my_millseconds;//毫秒

    std::chrono::microseconds us(3);//微妙
    my_millseconds ms = 2us; //微妙转换给毫秒, 产生小数

    也可以用来计算时间间隔: (直接加减)

    1
    2
    3
    4
    5
    6
    7
    std::chrono::minutes t1( 10 );
    std::chrono::seconds t2( 60 );
    std::chrono::seconds t3 = t1 - t2; //大单位赋值给了小单位, 没有小数产生
    std::cout << t3.count() << " second" << std::endl;

    std::cout << chrono::duration_cast<chrono::minutes>( t3 ).count()
    <<" minutes" << std::endl;
  • duration_cast()
    不同单位类型转换, 但是它只会简单的取整, 例如

    1
    2
    3
    4
    5
    6
    seconds s(30); //30秒, 转换成分, 是0.5分, 结果下面却输出0
    auto m = duration_cast<minutes>(s); // 0
    cout << m << endl; //duration对象可以直接输出

    seconds s(301);
    cout << duration_cast<minutes>(s)<< endl; //5

    最好是拿小单位去接收大单位的值, 因为不会有小数部分, 也就不存在被忽略.

  • 其他转换
    对已经存在的duration对象进行其他单位的转换操作(而不是简单的小数截断)
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
     //c++17标准
    floor() //取上限
    seil() //去下限
    round() //四舍五入

    //注意使用的时候按照类模板的方式(比Boost中更加丰富的模板参数)
    template <class ToDuration, class Rep, class Period>
    constexpr ToDuration floor(const duration<Rep, Period>& d);
    ``
    * 最大最小单位数.
    也就是某个计时单位的最大最小值. 一般取决于 `Rep`的类型.
    ```c++
    //duration 类的成员函数
    static constexpr duration max(); //返回最大的计时单位个数
    static constexpr duration min(); //返回最小的计时单位个数
    static constepr duration zero(); //返回0个单位的时间间隔

    //例如:
    //static constexpr duration min(); //duration类的成员方法
    //它返回 std::chrono::duration(std::chrono::duration_values<rep>::min())
    //而在std::chrono::duration_values类中的成员方法 min()返回
    //returns std::numeric_limits<Rep>::lowest()

clock

时钟, duration相当于一个自带单位的时间间隔统计, 那么时钟就是制定计时的仪器; (时间点time_point 记录具体的时间戳, 也是需要时钟的) .这个封装类, 一是要提供计时手段, 另外第一个重要的作用就是用来获取当前时间, 以及实现time_t和time_point的相互转换.

包含三种不同的时钟:

  • system_clock 从系统获取的时钟(但是系统时间可以被修改)
  • steady_clock 不能被修改的时钟(从开机开始计算)
  • high_resolution_clock 高精度时钟(实际上是system_clock或者steady_clock的别名)

steady_clock可以获取稳定可靠的时间间隔, 后一次调用now()的值和前一次的差值是不因为修改了系统时间而改变, 它保证了稳定的时间间隔.

主要方法:

  • 通过 now() 获取当前时间:

    1
    2
    3
    std::chrono::system_clock::time_point t1 = std::chrono::system_clock::now();
    //等价于
    std::chrono::time_point<system_clock> t1 = std::chrono::system_clock::now();
  • 通过 to_time_t() 可以将一个 time_point 转换为ctime. (记录时长从 duration 变成 long int)

    1
    2
    std::time_t now_c = std::chrono::system_clock::to_time_t(time_point);
    printf("%s\n", ctime(&now_c));
  • 通过from_time_t()来将一个 ctime 转换成 time_point

time_point

具体的时间点, 具体的某个时刻, 相对于epoch的经过的时间单位的统计. time_point表示一个时间点, 用来获取1970.1.1以来的秒数和当前的时间, 可以做一些时间的比较和算术运算, 可以和ctime库结合起来显示时间. time_point 必须要 clock 来计时, time_point 有一个函数 time_from_eproch() 用来获得1970年1月1日到 time_point 时间经过的 duration. 例如:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
#include <iostream>
#include <ratio>
#include <chrono>

//计算过了多少天
int main (void)
{
using namespace std::chrono;
typedef duration<int, std::ratio<60*60*24>> days_type; //天

//auto now = system_clock::now();
time_point<system_clock> now( system_clock::now());
time_point<system_clock, days_type> today = time_point_cast<days_type>(now);

std::cout << today.time_since_epoch().count() << " days since epoch" << std::endl;
return 0;
}

比较重要的方法就是:

  • duration time_since_epoch() const;
    案例代码如下:

    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 <iostream>
    #include <chrono>
    #include <ctime>

    int main(void)
    {

    using namespace std::chrono;

    time_point<system_clock> p2, p3;

    p2 = system_clock::now();
    p3 = p2 - hours(24); //昨天的此刻
    std::cout << "yesterday, hours since epoch: "
    << duration_cast<hours>(
    p3.time_since_epoch()).count()
    << std::endl;

    std::cout << "hours since epoch: "
    << duration_cast<hours>(
    p2.time_since_epoch()).count()
    << std::endl;
    return 0;
    }
  • time_point_cast
    把 time_point 从一种类型的 duration 转换成另一种单位类型的duration .
    定义如下:

    1
    2
    3
    4
    5
    6
    7
    template <class ToDuration, class Clock, class Duration>
    time_point<Clock, ToDuration> time_point_cast(
    const time_point<Clock, Duration> &t);
    ```
    其实现也非常简单:
    ```c++
    return time_point<Clock, ToDuration>(duration_cast<ToDuration>(t.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
    34
    #include <iostream>
    #include <chrono>

    using Clock = std::chrono::high_resolution_clock;
    using Ms = std::chrono::milliseconds;
    using Sec = std::chrono::seconds;

    //使用TimePoint, 要指定duration, 例如Ms或者Sec
    template<class Duration>
    using TimePoint = std::chrono::time_point<Clock, Duration>;

    inline void print_ms(const TimePoint<Ms>& time_point)
    {
    std::cout << time_point.time_since_epoch().count() << " ms\n";
    }

    int main()
    {
    TimePoint<Sec> time_point_sec(Sec(4));

    // implicit cast, no precision loss
    //(大单位向小单位转换)
    TimePoint<Ms> time_point_ms(time_point_sec);
    print_ms(time_point_ms); // 4000 ms

    time_point_ms = TimePoint<Ms>(Ms(5756));

    // explicit cast, need when precision loss may happens
    // 5756 truncated to 5000
    time_point_sec = std::chrono::time_point_cast<Sec>(time_point_ms);

    //调用的时候, 大单位向小单位赋值
    print_ms(time_point_sec); // 5000 ms
    }

综合案例

实现一个简单的 timer 计时器(一般采用steady_clock).

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
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
#include <iostream>
#include <ratio>
#include <chrono>

using namespace std;
using namespace std::chrono;


//默认以毫秒为单位
class Timer final
{

private:
typedef high_resolution_clock clock_type;
typedef milliseconds duration_type;
typedef long int64_t;

time_point<clock_type> m_begin;

public:

Timer() : m_begin(clock_type::now()) {}
~Timer() = default;

void restart() { m_begin = clock_type::now(); }

//默认输出毫秒
int64_t elapsed() const
{
/*return std::chrono::duration::round<duration_type>
(clock_type::now() - m_begin).count();
//编译器不支持 C++17
*/
return duration_cast<duration_type>(clock_type::now() - m_begin).count();
//直接返回 duration对象 也可以直接cout输出
}

//下面采用重载, 而不是模板参数, 避免用户传参麻烦

//微秒
int64_t elapsed_micro() const
{
return duration_cast<microseconds>(
clock_type::now() - m_begin).count();
}

//秒
int64_t elapsed_seconds() const
{
return duration_cast<seconds>(clock_type::now() - m_begin).count();
}

};


void fun()
{
for(int i = 0; i < 100; i++) {
cout << "hello word" << endl;
}

}

int main()
{
Timer t; //开始计时
fun();
cout<<t.elapsed_micro()<<endl; //打印微秒()
cout<<t.elapsed()<<endl; //打印fun函数耗时多少毫秒
cout<<t.elapsed_seconds()<<endl; //打印秒

return 0;
}

尾巴

C++这套库, 虽然只有三个clock, time_point, duration, 熟悉起来还是要花一段时间的.

文章目录
  1. 1. 引子
  2. 2. 正文
    1. 2.1. 相关概念
      1. 2.1.1. UTC
      2. 2.1.2. CST
      3. 2.1.3. DST
      4. 2.1.4. Epoch
      5. 2.1.5. CalendarTime
      6. 2.1.6. ClockTick
      7. 2.1.7. Second
    2. 2.2. C部分
      1. 2.2.1. 结构体定义
      2. 2.2.2. 函数定义
      3. 2.2.3. 综合案例
    3. 2.3. Cpp部分
      1. 2.3.1. duration
      2. 2.3.2. clock
      3. 2.3.3. time_point
      4. 2.3.4. 综合案例
  3. 3. 尾巴
|