1. 核心时间概念
(1)时间戳:从「Unix 纪元(1970-01-01 00:00:00 UTC)」到当前的秒数 / 毫秒数,是绝对时间。
(2)本地时间 / UTC
- 本地时间:操作系统时区对应的时间;
- UTC:世界协调时间(无时区偏移)
(3)时钟类型
- system_clock:系统墙钟(可被修改,如手动调时间),可转时间戳
- steady_clock:稳定时钟(不可修改),适合计算时间差
- high_resolution_clock:高精度时钟(通常是前两者的别名)
2. C 风格时间库:<ctime>
<ctime> 库基于 C 语言的 <time.h>,主要用于将时间点转换为人类可读的日期和时间字符串。
<ctime> 库的核心在于两种时间表示形式:
time_t: 表示自 Unix 纪元(1970 年 1 月 1 日 00:00:00 UTC) 以来经过的秒数。
struct tm: 一个包含日期和时间组件(年、月、日、时、分、秒等)的结构体。
2.1. 时间操作
2.1.1. clock()
返回程序启动到调用此函数时所消耗的处理器时间(CPU时间),单位是时钟滴答数 (clock ticks)。主要用于测量程序代码段的执行耗时,而不是实际的挂钟时间。
clock_t clock (void);
clock_t:能够表示时钟滴答数的基本算术类型的别名。
时钟滴答是恒定但系统特定长度的时间单位。
通过宏 CLOCKS_PER_SEC 转换为秒:CPU时间(秒) = clock() / (double)CLOCKS_PER_SEC
#include <iostream>
#include <ctime>
#include <unistd.h>
int main()
{
// 记录起始 CPU 时间
clock_t start = clock();
// 耗时操作
int sum = 0;
for (long long i = 0; i < 1e8; ++i) {
sum += i;
}
sleep(2); // 此段时间不占用 CPU,不会被统计
// 记录结束 CPU 时间
clock_t end = clock();
// 转换为秒
double cpu_time = (double)(end - start) / CLOCKS_PER_SEC;
std::cout << "CPU 耗时:" << cpu_time << " 秒" << std::endl;
return 0;
}
![]()
关键特性
- sleep()/usleep() 期间进程挂起,不占用 CPU,不计入返回值;
- 多线程程序中,返回所有线程 CPU 时间的总和;
- CLOCKS_PER_SEC 是标准宏(通常为 1000000,即 1 微秒 / 滴答)
注意:用 <ctime> 时,加 std:: 是符合 C++ 标准的写法,不加也能运行(编译器兼容),但推荐加以避免命名冲突。
2.1.2. time()
获取当前的日历时间 (time_t)。这是获取当前时间戳(自纪元以来的秒数)的主要方法。传入 NULL 或指向 time_t 变量的指针。
time_t time (time_t* timer);
参数:
- 传入 NULL:仅返回时间戳,不修改外部变量(最常用);
- 传入 time_t* 指针:将时间戳同时写入该指针指向的变量,返回值与该变量值一致
返回值:
成功:返回自 Unix 纪元(1970-01-01 00:00:00 UTC) 以来的秒数(time_t 类型)
失败:返回 (time_t)-1。
多数系统中 time_t 是 64 位整数(可表示到 292 亿年后),
32 位系统中 time_t 是 32 位整数,会在 2038 年溢出(2038 年问题),
需编译为 64 位程序(-m64)。
#include <iostream>
#include <ctime>
#include <unistd.h>
int main()
{
time_t begin = time(NULL);
int sum = 0;
for (long long i = 0; i < 1e8; ++i) {
sum += i;
}
sleep(2);
time_t end = time(NULL);
std::cout << "程序开始时间:" << begin << std::endl;
std::cout << "程序结束时间:" << end << std::endl;
time_t ret = time(&begin);
std::cout << "ret:" << ret << std::endl;
return 0;
}

核心特性:
- 统计的是系统真实流逝时间(墙上时钟),包含 sleep()/ 进程挂起时间;
- 精度为秒级,无法获取毫秒 / 微秒级时间;
- 受系统时区 / 时间设置影响(但时间戳本身是 UTC 基准)
2.1.3. difftime()
C/C++ 标准库中专门用于计算两个 time_t 时间戳差值的函数,核心特点是返回高精度浮点数,避免直接减法的潜在问题。
double difftime (time_t end, time_t beginning);
- end:结束时间戳
- beginning:起始时间戳
返回值
以秒为单位的时间差(double 类型),支持小数(如 2.5 秒),
本质等价于 (double)(time_end - time_beg)
#include <iostream>
#include <ctime>
int main()
{
time_t begin = time(NULL);
int sum = 0;
for (long long i = 0; i < 1e8; ++i) {
sum += i;
}
sleep(2);
time_t end = time(NULL);
std::cout << "程序运行时间:" << end - begin << std::endl;
std::cout << "程序运行时间时间:" << difftime(end, begin) << std::endl;
return 0;
}

特性:
精度限制:difftime() 的精度由 time_t 决定 —— 若 time_t 仅精确到秒(绝大多数系统),返回值的小数部分始终为 0;只有当 time_t 支持毫秒 / 微秒级时,小数部分才有效。
溢出安全:若 time_t 是 32 位有符号整数(范围:1901~2038),两个时间戳差值过大时直接减法会溢出,而 difftime() 先将 time_t 转为 double(范围远大于 32 位整数),避免溢出:
2.1.4. mktime()
用于将本地时间的 tm 结构体转换为 time_t 时间戳。通过 time_t 还可以补全 tm 的星期 / 年内天数。
time_t mktime (struct tm * timeptr);
timeptr:指向 tm 结构体的指针,该结构体需填充本地时间的年/月/日/时/分/秒等字段
成功:返回对应时间的 time_t 时间戳(自 1970-01-01 UTC 的秒数);
失败:返回 (time_t)-1
struct tm
{
int tm_sec; /* Seconds (0-60) */
int tm_min; /* Minutes (0-59) */
int tm_hour; /* Hours (0-23) */
int tm_mday; /* Day of the month (1-31) */
int tm_mon; /* Month (0-11) */
int tm_year; /* Year - 1900 */
int tm_wday; /* Day of the week (0-6, Sunday = 0) */未赋值时 mktime() 自动计算
int tm_yday; /* Day in the year (0-365, 1 Jan = 0) */未赋值时 mktime() 自动计算
int tm_isdst; /* Daylight saving time */
};
tm_isdst 是用于标记夏令时 状态的字段
1 明确表示当前时间处于夏令时时段(夏令时生效,时钟调快 1 小时);
0 明确表示当前时间不处于夏令时时段(标准时间);
-1 不指定夏令时状态,让系统自动检测 / 计算当前时间是否适用夏令时(默认推荐值);
当给 tm_yday(年内天数)、tm_wday(星期数)赋值错误时,mktime() 会直接忽略这些错误值,并根据 tm_year/tm_mon/tm_mday 等核心字段重新计算、覆盖为正确值。
int main()
{
struct tm t = {};
t.tm_year = 125;
t.tm_mon = 11; // 0~11
t.tm_mday = 6;
t.tm_hour = 14;
t.tm_min = 19;
t.tm_sec = 30;
t.tm_yday = 1;
t.tm_wday = 1;
time_t utctime = mktime(&t);
std::cout << "2025-12-6 14::19::30 的时间戳:" << utctime << std::endl;
std::cout << "2025-12-06 是星期:" << t.tm_wday << "(0=周日,6=周六)" << std::endl;
std::cout << "2025-12-06 是年内第:" << t.tm_yday << " 天(0=1月1日)" << std::endl;
return 0;
}

特性:
1. 自动规范化:修正 tm 中的非法值(如月份 13→次年 1 月、日期 32→次月 2 日);
2. 填充补全:若 tm_wday(星期)、tm_yday(年内天数)未赋值,会自动计算并填充;
3. 本地时区敏感:转换结果受系统本地时区影响(如北京时间 UTC+8)
2.2. 时间格式转换函数
2.2.1. localtime()
将 time_t 值转换为表示 本地时间 的 struct tm 结构体。
struct tm * localtime (const time_t * timer);
参数 timep:指向 time_t 时间戳的指针(通常由 time() 获取);
返回值:指向静态 tm 结构体的指针(成功),或 NULL(失败,如时间戳超出范围)。
int main()
{
time_t tt = time(NULL);
struct tm *local_tm = localtime(&tt);
std::cout << "当前本地时间:"
<< (local_tm->tm_year + 1900) << "-"
<< (local_tm->tm_mon + 1) << "-"
<< local_tm->tm_mday << " "
<< local_tm->tm_hour << ":"
<< local_tm->tm_min << ":"
<< local_tm->tm_sec << std::endl;
return 0;
}
![]()
注意事项:
1. 静态缓冲区的覆盖问题(核心风险)
localtime 返回的 tm 结构体是全局静态内存,后续调用 localtime/gmtime/ctime 等函数会覆盖之前的结果。例如:
std::time_t t1 = ...;
std::time_t t2 = ...;
std::tm* tm1 = std::localtime(&t1); // tm1指向静态缓冲区
std::tm* tm2 = std::localtime(&t2); // 静态缓冲区被覆盖,tm1和tm2的内容都会变成t2的时间
2. 线程不安全
由于使用静态缓冲区,多线程同时调用 localtime 会导致数据竞争,在Linux/macOS中可使用localtime_r(POSIX 标准)替代。
3. 时间范围限制
32 位 time_t:仅支持 1970-01-01 ~ 2038-01-19;
64 位 time_t:支持到 3000-12-31(现代系统默认是 64 位)。
若时间戳超出范围,localtime 返回 NULL。
2.2.2. gmtime()
将 time_t 时间戳转换为 UTC 时区 tm 结构体.
struct tm * gmtime (const time_t * timer);
返回值:指向静态 tm 结构体的指针(成功),或 NULL(失败,如时间戳超出范围)。
#define UTC (0)
#define CCT (+8)
int main() {
time_t now = time(nullptr);
struct tm* ptm = gmtime(&now);
printf ("Reykjavik (Iceland) : %2d:%02d\n", (ptm->tm_hour+UTC)%24, ptm->tm_min);
printf ("Beijing (China) : %2d:%02d\n", (ptm->tm_hour+CCT)%24, ptm->tm_min);
return 0;
}

同样存在静态缓冲区的覆盖、线程不安去等问题,Linux/macOS(POSIX)中,可使用gmtime_r。
2.2.3. asctime()
把 tm结构体(时间结构体)转换成可读的字符串,固定格式为 Www Mmm dd hh:mm:ss yyyy\n
如Wed Jun 30 21:49:08 1993。
char* asctime (const struct tm * timeptr);
返回值:指向静态字符串的指针(格式固定为 26 字符,含换行符 \n 和结束符 \0);
失败返回:NULL(极少发生,仅 tm 结构体字段非法且无法解析时)。
int main()
{
time_t now = time(NULL);
struct tm * utc_tm = gmtime(&now);
char* asctm = asctime(utc_tm);
std::cout << "UTC 时间字符串:" << asctm;
return 0;
}
![]()
同样存在静态缓冲区覆盖和线程不安全问题,
2.2.4. ctime()
直接将 time_t 时间戳转换为本地时区固定格式字符串,ctime() 始终输出本地时区时间。
char* ctime (const time_t * timer);
返回值:指向静态字符串的指针(格式与 asctime() 完全一致,26 字符,含 \n 和 \0);
底层逻辑:ctime(timep) = asctime(localtime(timep))(先转本地时区 tm 结构体,再转字符串)。
int main()
{
time_t now = time(NULL);
char* tm_str = ctime(&now);
std::cout << tm_str;
return 0;
}
![]()
2.2.5. strftime()
高度灵活的时间格式化函数。使用指定的格式字符串将 struct tm 中的时间信息转换为自定义格式的字符串。
size_t strftime (char* ptr, size_t maxsize, const char* format,
const struct tm* timeptr );
str 输出缓冲区(自定义数组),存储格式化后的字符串
maxsize 缓冲区最大长度(避免溢出)
format 格式控制字符串(含普通字符 + 格式占位符,如 %Y-%m-%d %H:%M:%S)
timeptr 指向 tm 结构体的指针(可由 localtime()/gmtime() 生成)
%Y 4 位年份
%y 2 位年份(00~99) 25
%m 月份(01~12)
%d 日期(01~31)
%H 小时(24 小时制,00~23)
%I 小时(12 小时制,01~12)
%M 分钟(00~59)
%S 秒(00~59)
%a 星期缩写(本地语言) Sat / 周六
%A 星期全称(本地语言) Saturday / 星期六
%b 月份缩写(本地语言) Dec / 12 月
%B 月份全称(本地语言) December / 十二月
%p 上午 / 下午(12 小时制) PM / 下午
%z 时区偏移(如 +0800) +0800
%Z 时区名称(如 CST) CST
int main()
{
time_t tt = time(NULL);
struct tm *lt = localtime(&tt);
char buf[64];
strftime(buf, sizeof(buf) - 1, "%Y-%m-%d %H:%M:%S %p", lt);
std::cout << buf << std::endl;
return 0;
}
![]()
3. C++chrono库
<chrono> 是 C++11 引入的现代时间处理库,核心解决了传统 C 风格时间函数(time()/gmtime() 等)的痛点:类型不安全、精度固定、时区处理繁琐、无面向对象封装。C++14 进一步补充了小数秒、字面量等特性。C++20 引入了日历和时区支持,极大地增强了功能。
<chrono> 库提供了三个核心概念:时长 (Duration)、时钟 (Clock) 、时间点 (Time Point) 。
3.1. 时长(duration)
std::chrono::duration 表示一段时间,由一个计数 (count) 和一个时间单位 (tick period) 组成。
模板原型:template <class Rep, class Period = ratio<1>> class duration;
Rep:存储时间数值的类型(如 int/long/double);
Period:时间单位(ratio<1>= 秒,ratio<1,1000>= 毫秒,ratio<1,1000000>= 微秒)。
chrono::nanoseconds 纳秒 duration<long long, ratio<1,1e9>>
chrono::microseconds 微秒 duration<long long, ratio<1,1e6>>
chrono::milliseconds 毫秒 duration<long long, ratio<1,1000>>
chrono::seconds 秒 duration<long long>
chrono::minutes 分钟 duration<long long, ratio<60>>
chrono::hours 小时 duration<long long, ratio<3600>>
时长相加的类型推导规则:std::chrono::duration(时长)的加法运算遵循「低精度向高精度转换」原则。
<chrono> 库对「时长 ÷ 数值」的重载规则和单位缩放逻辑:
数值层面:时长的计数值(count())除以该数;
单位层面:时长的单位(Period)乘以该数(即缩放单位);
#include <iostream>
#include <chrono>
int main()
{
std::chrono::seconds s(5);
std::chrono::milliseconds m(1500);
auto msm = s + m;
auto ssm = msm / 1000;
std::cout << "5秒 + 1500毫秒=" << msm.count() << "毫秒" << std::endl;
std::cout << "转换为秒:" << ssm.count() << std::endl;
std::chrono::seconds s_cast = std::chrono::duration_cast<std::chrono::seconds>(msm);
std::cout << "s_cast:" << s_cast.count() << std::endl;
return 0;
}

3.2. 时间点(Time Point)
时间点是 C++11 <chrono> 库中表示「某个具体时刻」的类模板,核心是绑定时钟 + 记录从时钟纪元开始的时长
template <class Clock, class Duration = typename Clock::duration>
class time_point;
Clock:时间参考系(如 system_clock/steady_clock);
Duration:从时钟「纪元」到该时间点的时长(如秒 / 毫秒)。
system_clock:纪元为 1970-01-01 UTC(与 time_t 兼容);
steady_clock:无固定纪元,仅相对值有效(计时专用)。
常用操作
获取当前时间点 auto tp = std::chrono::system_clock::now();
转 time_t(秒级) time_t t = std::chrono::system_clock::to_time_t(tp);
从 time_t 转回 auto tp = std::chrono::system_clock::from_time_t(t);
时间点运算 auto tp2 = tp1 + std::chrono::seconds(10);(10 秒后)
计算时间差 auto dur = tp2 - tp1;(结果为 duration 类型)
system_clock::now() 含毫秒 / 微秒精度,转 time_t 会丢失小数秒;
3.3. 时钟(Clock)
<chrono> 提供 3 种核心时钟,适配不同场景:
system_clock:对应系统时间(可手动修改),纪元为 1970-01-01 UTC(Unix 时间戳
steady_clock:单调递增时钟(不受系统时间修改影响),精度高(纳秒级)
high_resolution_clock:系统最高精度时钟(通常是 steady_clock 或 system_clock 的别名)
system_clock
system_clock::now():获取当前系统时间的 time_point(时间点对象)
system_clock::to_time_t(tp):将 system_clock 的时间点 tp 转换为传统 time_t 时间戳(秒级)
system_clock::from_time_t(t):将 time_t 时间戳 t 转换为 system_clock 的时间点(反向兼容)
steady_clock
steady_clock::now():获取单调递增时钟当前时间点
特性
单调性 时间永不回退,now() 返回值只增不减(核心优势)
纪元 无固定纪元(仅相对时间差有效,无法转换为日历时间)
精度 系统最高精度(通常纳秒级)
核心用途 性能测试、超时判断、帧率统计等 “相对计时” 场景
不可转换 time_t 无 to_time_t/from_time_t 接口(无绝对时间意义)
#include <iostream>
#include <chrono>
#include <thread>
#include <ctime>
int main()
{
auto begin = std::chrono::steady_clock::now();
time_t t1 = std::chrono::system_clock::to_time_t(std::chrono::system_clock::now());
std::cout << t1 << std::endl;
std::this_thread::sleep_for(std::chrono::milliseconds(150));
int sum = 0;
for (long long i = 0; i < 1e8; ++i) sum += i;
auto end = std::chrono::steady_clock::now();
time_t t2 = std::chrono::system_clock::to_time_t(std::chrono::system_clock::now());
auto duration_ms = std::chrono::duration_cast<std::chrono::milliseconds>(end - begin);
std::cout << t2 << std::endl;
std::cout << duration_ms.count() << " 毫秒" << std::endl;
return 0;
}
1611

被折叠的 条评论
为什么被折叠?



