C++ 标准库 ctime

下面对 C++ 标准库中 <ctime> 头文件提供的日期时间类型与函数做一次系统、深入的梳理,包括类型定义、常用函数、格式化输入/输出、安全性问题、以及实践建议。


一、概述

  • <ctime>(等同于 C 语言的 <time.h>)定义了表示与操作时间的基本类型和函数。
  • 主要用途包括获取当前时间、实现时间差计算、在日历(年/月/日、时分秒)与原始时间戳间转换,以及将时间格式化为字符串或从字符串解析时间等。
  • 底层依赖于平台 C 运行时,通常以秒为基本单位;注意各函数的线程安全性与时区影响。

二、主要类型

类型描述
std::time_t以实现定义的整数或浮点类型保存的“原始”时间戳,通常为自 1970‑01‑01 00:00:00 UTC 起的秒数。
std::clock_t用于 std::clock() 返回的 CPU 时间计数;与 CLOCKS_PER_SEC 配合使用,单位通常是时钟滴答。
std::tm结构体,按日历分解时间,包含年月日、时分秒、周、年中日数、夏令时标志等字段。
std::size_t用于 strftime 等函数的计数参数。
struct std::tm {
    int tm_sec;    // 秒   [0,60](允许闰秒)
    int tm_min;    // 分   [0,59]
    int tm_hour;   // 时   [0,23]
    int tm_mday;   // 一月中的日  [1,31]
    int tm_mon;    // 月   [0,11]
    int tm_year;   // 自 1900 年起的年数
    int tm_wday;   // 一周中的日 [0,6] 0=星期日
    int tm_yday;   // 年中日  [0,365]
    int tm_isdst;  // 夏令时标志 >0=生效,0=不生效,<0=未知
};

三、获取当前时间

1. std::time

std::time_t now = std::time(nullptr);
  • 功能:返回当前日历时间(time_t),或将其写入传入指针并返回。
  • 复杂度:常数时间。
  • 注意:受系统时钟影响,可通过 std::gmtime/std::localtime 转换为日历时间。

2. std::clock

std::clock_t c = std::clock();
double cpu_seconds = double(c) / CLOCKS_PER_SEC;
  • 功能:返回程序执行以来所消耗的处理器时钟滴答数。
  • CLOCKS_PER_SEC 定义每秒的时钟滴答数,常见值是 1e6 或 1e3。
  • 用途:用于测量 CPU 时间(非实时),注意在长时间运行下可能溢出。

四、时间戳与日历时间相互转换

1. std::gmtime / std::localtime

std::tm* utc     = std::gmtime(&now);   // 转换为 UTC 日历时间
std::tm* local  = std::localtime(&now); // 转换为本地时区日历时间
  • 返回:指向静态 std::tm 对象的指针(非线程安全)。
  • 线程安全版本(C11/C++20 或 POSIX):
    • std::gmtime_s(&tm, &now) / std::localtime_s(&tm, &now)(Windows)
    • std::gmtime_r(&now, &tm) / std::localtime_r(&now, &tm)(POSIX)

2. std::mktime

std::tm tm = *local;
std::time_t t2 = std::mktime(&tm);
  • 功能:将本地时区的 std::tm 转换回 time_t,并标准化各字段(如归一化秒/分钟越界、设置夏令时标志)。
  • 注意tm_isdst 可设为 -1 让实现自行判断夏令时;该函数会修改传入的 tm 结构。

五、字符串格式化与解析

1. std::asctime / std::ctime

char* s1 = std::asctime(&tm);    // "Wed Jun 30 21:49:08 1993\n\0"
char* s2 = std::ctime(&now);     // 等价于 asctime(gmtime/localtime)
  • 返回:静态缓冲区指针,非线程安全。
  • 线程安全版本asctime_s / ctime_s(Windows),或手动用 std::strftime

2. std::strftime

char buf[100];
std::size_t len = std::strftime(
    buf, sizeof(buf),
    "%Y-%m-%d %H:%M:%S",    // 格式化模板
    &tm
);
// buf = "2025-07-12 14:30:59"
  • 功能:根据 std::tm 及格式字符串,生成格式化日期时间文本。
  • 格式说明符(常用):
    • %Y 年(四位)
    • %m 月(01–12)
    • %d 日(01–31)
    • %H 时(00–23)
    • %M 分(00–59)
    • %S 秒(00–60,允许闰秒)
    • %z 时区偏移(如 +0800
    • %Z 时区名称
    • %a 星期缩写(如 Mon
    • %A 星期全称
  • 返回:写入字符数,不含终止 \0;若缓冲区不足,返回 0。

3. 解析字符串到 std::tm

C++17 标准尚未直接提供解析函数,需借助 C 库或自定义:

std::istringstream iss("2025-07-12 14:30:59");
std::tm tm = {};
iss >> std::get_time(&tm, "%Y-%m-%d %H:%M:%S");
if (iss.fail()) { /* 解析失败 */ }
  • 需包含 <iomanip>std::get_time 从流中按指定格式填充 std::tm

六、时间差计算

1. std::difftime

double seconds = std::difftime(t2, t1);
  • 功能:计算两个 time_t 之间的差值(t2 - t1),以秒为单位返回 double
  • 优势:避免直接做整数减法导致的溢出或类型问题。

七、实践建议

  1. 线程安全
    • 避免使用 std::gmtimestd::localtimestd::asctimestd::ctime,改用带 _s(Windows)或 _r(POSIX)的安全版本,或直接用 std::strftime/std::get_time
  2. 时区与夏令时
    • 对跨时区应用,要统一使用 UTC(gmtime)存储与计算,展示时才转换为本地时区。
    • 夏令时标志由 tm_isdst 控制,通常设为 -1 让库函数自动计算。
  3. 高精度计时
    • std::clock 仅度量 CPU 时间,分辨率和溢出可能不足。对高精度或实时测量,应使用 <chrono>(如 steady_clockhigh_resolution_clock)。
  4. 避免缓冲区溢出
    • strftime 时确保提供足够大的缓冲区,并检查返回值非零。
  5. 解析格式灵活性
    • std::get_time 对流输入格式严格;对复杂输入可考虑正则或第三方库(如 Howard Hinnant 的 date 库)。

通过以上对 <ctime> 中类型、转换函数、字符串格式化/解析及注意事项的全面梳理,相信能帮助你在日志记录、调度系统、时间戳管理等场景中安全、正确地使用标准库提供的时间功能。祝编码顺利!

类似文章

发表回复

您的邮箱地址不会被公开。 必填项已用 * 标注