C 语言 time.h
更新时间:2026-08-26。本文是
languages/c/主题入门层第 68 篇。程序要记时间戳、算耗时、排定时任务——time.h就是时间工具箱。但 C 的时间 API 出了名的绕:time_t、struct tm、clock_t、timespec,四种类型来回倒腾。这篇把它们的关系理清,顺手解决"怎么测代码跑多久"。
本文要回答的问题
- C 里有哪些时间类型?怎么互相转换?
- 怎么格式化输出当前时间?
- 怎么准确测量一段代码的耗时?
一、三种时间表示
| 类型 | 含义 | 来源 |
|---|---|---|
time_t | 自 1970-01-01 起的秒数 | time(NULL) |
struct tm | 拆开的年月日时分秒 | localtime/gmtime |
struct timespec | 秒 + 纳秒 | clock_gettime |
c
#include <time.h>
time_t now = time(NULL); // 秒级时间戳
printf("%ld\n", (long)now); // 1756195200 之类二、time_t → struct tm:拆开看
c
time_t now = time(NULL);
struct tm *t = localtime(&now); // 本地时间(北京时间等)
// struct tm *t = gmtime(&now); // UTC 时间
printf("%d-%02d-%02d %02d:%02d:%02d\n",
t->tm_year + 1900, // 年份从 1900 起算,要加
t->tm_mon + 1, // 月份 0~11,要加 1
t->tm_mday,
t->tm_hour, t->tm_min, t->tm_sec);两个 +1:tm_year 从 1900 起算(2026 → 126)、tm_mon 从 0 起算(1 月 → 0)。这是时间 API 最经典的"差一错误"来源。
三、strftime:格式化输出
c
time_t now = time(NULL);
struct tm *t = localtime(&now);
char buf[64];
strftime(buf, sizeof(buf), "%Y-%m-%d %H:%M:%S", t);
printf("%s\n", buf); // 2026-08-26 14:30:00常用格式符:%Y 年、%m 月、%d 日、%H 时(24h)、%M 分、%S 秒、%F 等价 %Y-%m-%d。比手动拼格式安全,不会踩 +1 的坑。
四、测量耗时:clock_gettime
c
#include <time.h>
struct timespec start, end;
clock_gettime(CLOCK_MONOTONIC, &start);
/* 要测量的代码 */
clock_gettime(CLOCK_MONOTONIC, &end);
double elapsed = (end.tv_sec - start.tv_sec)
+ (end.tv_nsec - start.tv_nsec) / 1e9;
printf("elapsed: %.6f s\n", elapsed);要点:
CLOCK_MONOTONIC单调时钟,不受系统时间修改影响——测耗时用它,别用time();tv_sec秒 +tv_nsec纳秒,组合成小数秒。
注意 time() 是秒级,clock() 是 CPU 时间,毫秒以下的性能测量必须 clock_gettime。
五、时间运算
c
// 当前时间加 1 天(86400 秒)
time_t future = time(NULL) + 86400;
struct tm *t = localtime(&future);
// 计算两个时间差(秒)
double diff = difftime(future, time(NULL));秒数运算简单粗暴但要注意夏令时等边界;复杂日期运算(下个月 1 号之类)用 mktime 归一化:
c
struct tm t = *localtime(&now);
t.tm_mday += 7; // 加 7 天,字段可能溢出
mktime(&t); // 归一化成合法日期六、常见坑对照
| 坑 | 现象 | 对策 |
|---|---|---|
| 忘 tm_year + 1900 | 年份差 1900 | 转换时补 |
| 忘 tm_mon + 1 | 月份差 1 | 转换时补 |
| 用 time() 测毫秒级 | 结果恒 0 | clock_gettime |
| 用 CLOCK_REALTIME 测耗时 | 改系统时间结果错 | CLOCK_MONOTONIC |
| 直接存 localtime 返回值 | 共享静态区被覆盖 | 尽快复制或用 localtime_r |
七、与本站主线衔接
- 性能测量在本站是核心话题,配合perf 使用指南与时间测量精度食用;
- 随机种子用时间初始化,见rand 与 srand;
- 日志时间戳实践,见写日志与调试输出。
一句话总结
time() 拿秒级时间戳,localtime 拆成 struct tm(记得 tm_year+1900、tm_mon+1),strftime 安全格式化,测耗时用 clock_gettime(CLOCK_MONOTONIC, ...)——时间的坑都在"类型转换"和"时区/基准"上,选对 API 就赢了一半。