优先使用C++20的std::chrono::year_month_day,它提供类型安全、无时区副作用的纯日期结构体,可直接通过.year()、.month()、.day()获取整数值;若需兼容旧标准,则用std::chrono::system_clock::now()转time_t后调用线程安全的localtime_r(POSIX)或localtime_s(Windows)。

用 std::chrono + std::tm 获取本地当前日期结构体
标准 C++ 没有直接返回“日期结构体”的函数,但能组合 std::chrono::system_clock::now() 和 std::localtime() 得到含年月日的 std::tm。注意:std::localtime() 是线程不安全的,多线程下必须用 std::localtime_r()(POSIX)或 std::localtime_s()(Windows/MSVC)。
-
std::tm的tm_year是从 1900 起算(2024 → 124),tm_mon是 0–11(1 月 → 0) - 必须先转成
time_t,再调用时区转换函数;不能直接对std::chrono::time_point成员取值 - 示例代码(跨平台安全写法):
std::tm get_local_date() {
auto now = std::chrono::system_clock::now();
std::time_t t = std::chrono::system_clock::to_time_t(now);
std::tm tm_buf{};
#ifdef _WIN32
std::localtime_s(&tm_buf, &t);
#else
localtime_r(&t, &tm_buf);
#endif
return tm_buf;
}
用 std::chrono::year_month_day(C++20)获取纯日期结构体
C++20 引入了类型安全、无时区副作用的日期类型,std::chrono::year_month_day 就是真正的“日期结构体”,不含时间、不依赖 std::tm,且可直接比较、格式化。
- 需先通过
std::chrono::floor<:chrono::days>(now)</:chrono::days>截断到天级精度 - 构造后可用
.year()、.month()、.day()成员函数直接取整数值(无需偏移修正) - 不支持 Windows MSVC 旧版本(需 /std:c++20 且工具链 ≥ VS2019 16.10)
- 示例:
auto now = std::chrono::system_clock::now();
auto ymd = std::chrono::year_month_day{std::chrono::floor<std::chrono::days>(now)};
int y = static_cast<int>(ymd.year());
unsigned m = ymd.month();
unsigned d = ymd.day();
为什么不用 ctime() 或直接解析字符串
ctime() 返回的是带时区和星期的字符串(如 "Wed Apr 10 15:23:42 2024\n"),解析它来提取年月日既脆弱又低效——空格数、缩写长度、本地化语言都可能破坏匹配逻辑。
- 字符串解析无法处理闰秒、夏令时跳变等边界情况
- 正则或
sscanf()在不同 locale 下行为不一致(比如德语系统月份缩写是 “Apr” 还是 “Apr.”?) - 若只需结构化日期字段,绕路字符串是典型的“把简单问题复杂化”
容易被忽略的时区与线程安全细节
很多人默认 std::localtime() 就是“当前系统时区”,但实际它读取的是进程启动时缓存的 TZ 环境变量;运行中修改 TZ 不会自动生效。更隐蔽的问题是:多个线程并发调用未加锁的 std::localtime(),会共享内部静态缓冲区,导致返回的 std::tm* 指向同一块内存,结果相互覆盖。
立即学习“C++免费学习笔记(深入)”;
- Linux/macOS 必须用
localtime_r(),传入用户分配的std::tm*缓冲区 - Windows 上
localtime_s()第一个参数是输出缓冲区指针,第二个是输入time_t*,顺序和 POSIX 相反 - 如果项目已用
std::chrono为主时间库,优先选 C++20 的year_month_day,彻底避开 C 风格时区函数的坑


















