C++11
的日期和时间编程内容在 C++ Primer(第五版)这本书并没有介绍,目前网上的文章又大多质量堪忧或者不成系统,故写下这篇文章用作自己的技术沉淀和技术分享,大部分内容来自网上资料,文末也给出了参考链接。
日期和时间库是每个编程语言都会提供的内部库,其可以用打印模块耗时,从而方便做性能分析,也可以用作打印运行时间点。本文的内容着重于 C++11-C++17的内容,C++20的日期和时钟库虽然使用更方便也更强大,但是考虑到版本兼容和程序移植问题,故不做深入探讨。
一,概述
C++ 中可以使用的日期时间 API 分为两类:
C-style
日期时间库,位于头文件中。这是原先 <time.h> 头文件的 C++ 版本。 chrono
库:C++ 11 中新增API,增加了时间点,时长和时钟等相关接口(使用较为复杂)。
在 C++11 之前,C++ 编程只能使用 C-style 日期时间库,其精度只有秒级别,这对于有高精度要求的程序来说,是不够的。但这个问题在C++11 中得到了解决,C++11 中不仅扩展了对于精度的要求,也为不同系统的时间要求提供了支持。另一方面,对于只能使用 C-style 日期时间库的程序来说,C++17 中也增加了 timespec 将精度提升到了纳秒级别。
二,C-style 日期和时间库
#include <ctime>
该头文件包含了获取和操作日期和时间的函数和相关数据类型定义。
2.1,数据类型
名称 | 说明 |
---|---|
time_t |
能够表示时间的基本算术类型的别名,能够表示函数 time 返回的时间,单位为秒级别。 |
clock_t |
能够表示时钟滴答计数的基本算术类型的别名(可用作进程运行时间) |
size_t |
sizeof 运算符返回的无符号整数类型。 |
struct tm |
包含日历日期和时间的结构体类型 |
timespec* | 以秒和纳秒表示的时间 |
2.2,函数
C-style
日期时间库中包含的时间操作函数如下:
函数 | 说明 |
---|---|
std::clock_t clock() |
返回自程序启动时起的处理器时钟时间 |
double difftime(std::time_t time_end, std::time_t time_beg) |
计算开始和结束之间的秒数差 |
std::time_t time (time_t* timer) |
返回自纪元起计的系统当前时间, 函数可以为空指针 |
std::time_t mktime (struct tm * timeptr) |
将 tm 格式的时间转换成 time_t 表示的时间 |
时间转换函数如下:
函数 | 说明 |
---|---|
char* asctime(const struct tm* timeptr) |
将 tm 结构体对象转换为字符串的文本 |
char* ctime(const time_t* timer) |
将 time_t 对象转换为 C 字符串,用于表示日历时间 |
struct tm* gmtime(const time_t* time) |
将 time_t 转换成 UTC 表示的时间 |
struct tm* localtime(const time_t* timer) |
将 time_t 转换成本地时间 |
localtime
函数使用参数timer
指向的值来填充tm
结构体,其中的值表示对应的时间,以本地时区表示。
strftime
和 wcsftime
函数一般不常用,故不做介绍。tm
结构体的一般定义如下:
/* Used by other time functions. */
struct tm
{
int tm_sec; /* Seconds. [0-60] (1 leap second) */
int tm_min; /* Minutes. [0-59] */
int tm_hour; /* Hours. [0-23] */
int tm_mday; /* Day. [1-31] */
int tm_mon; /* Month. [0-11] */
int tm_year; /* Year - 1900. */
int tm_wday; /* Day of week. [0-6] */
int tm_yday; /* Days in year.[0-365] */
int tm_isdst; /* DST. [-1/0/1]*/
};
2.3,数据类型与函数关系梳理
时间和日期相关的函数及数据类型比较多,单纯看表格和代码不是很好记忆,第一个参考链接的作者给出了如下所示的思维导图,方便记忆与理解上面所有函数及数据类型之间各自的联系。
在这幅图中,以数据类型为中心,带方向的实线箭头表示该函数能返回相应类型的结果。
clock
函数是相对独立的一个函数,它返回进程运行的时间,具体描述见下文。time_t
描述了纪元时间,通过time
函数可以获得它,但它只能精确到秒级别。timespec
类型在time_t
的基础上,增加了纳秒的精度,通过timespec_get
获取。这是C++17
上新增的特性。tm
是日历类型,因为它其中包含了年月日等信息。通过 gmtime,localtime 和 mktime 函数可以将 time_t 和 tm 类型互相转换。- 考虑到时区的差异,因此存在 gmtime 和 localtime 两个函数。
- 无论是
time_t
还是tm
结构,都可以将其以字符串格式输出。ctime 和 asctime 输出的格式是固定的。如果需要自定义格式,需要使用 strftime 或者 wcsftime 函数。
2.4,时间类型
2.4.1,UTC 时间
协调世界时(Coordinated Universial Time,简称 UTC)是最主要的时间标准,其以原子时秒长为基础,在时刻上尽量接近于格林威治标准时间。
协调世界时是世界上调节时钟和时间的主要时间标准,它与0度经线的平太阳时相差不超过 1 秒。因此UTC时间+8即可获得北京标准时间(UTC+8)。
2.4.2,本地时间
本地时间与当地的时区相关,例如中国当地时间采用了北京标准时间(UTC+8
)。
2.4.3,纪元时间
纪元时间(Epoch time)又叫做 Unix 时间或者 POSIX 时间。它表示自1970 年 1 月 1 日 00:00 UTC 以来所经过的秒数(不考虑闰秒)。它在操作系统和文件格式中被广泛使用。
纪元时间这个想法很简单:以一个时间为起点加上一个偏移量便可以表达任何一个其他的时间。
为什么选这个时间作为起点,可以点击这里:Why is 1/1/1970 the “epoch time”?。
通过 time
函数获取当前时刻的纪元时间示例代码如