应使用 std::chrono::system_clock::to_time_t 转换时间点,再用线程安全的 localtime_r(POSIX)或 localtime_s(Windows)转为 std::tm,最后以 std::strftime 格式化为“YYYY-MM-DD”;C++20 可用 floor<days> + year_month_day 简化。

用 std::chrono + std::strftime 提取纯日期字符串
直接用 std::chrono::system_clock::now() 拿到的是带时分秒毫秒的完整时间点,必须先截断到日粒度。C++20 之前没有原生“只取日期”类型,所以得靠 std::tm 中转:先转成本地时区的 std::tm,再用 std::strftime 格式化为不含时间的部分。
常见错误是直接对 system_clock::time_point 调用 std::localtime —— 它只接受 time_t,得先用 .time_since_epoch().count() 转换再除以秒单位(注意精度丢失);更稳妥的做法是用 std::time_t t = std::chrono::system_clock::to_time_t(now)。
示例:
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
localtime_s(&tm_buf, &t); // Windows 安全版本
#else
localtime_r(&t, &tm_buf); // Linux/macOS 线程安全版本
#endif
char buf[11]; // "YYYY-MM-DD\0" 共 11 字节
std::strftime(buf, sizeof(buf), "%Y-%m-%d", &tm_buf);
std::string date_str = buf; // 如 "2024-05-22"
为什么不用 std::put_time 直接格式化?
std::put_time 看似简洁,但它底层仍依赖 std::tm*,且不处理线程安全问题。在多线程环境里,若多个线程同时调用 std::localtime(非 _r/_s 版本),会共享静态 std::tm 缓冲区,导致日期错乱——比如线程 A 取到 2024-05-22,线程 B 同时写入 2024-05-23,A 最终输出变成后者。
立即学习“C++免费学习笔记(深入)”;
- 务必使用
localtime_r(POSIX)或localtime_s(Windows) -
std::put_time的流操作符(<<)无法控制缓冲区大小,容易溢出;std::strftime显式传入缓冲区长度,更可控 - C++20 引入了
std::chrono::year_month_day,但需配合sys_days才能从system_clock转换,且仍要手动拼字符串或用std::format(C++23)
跨平台编译时要注意的宏和头文件
Windows 默认不启用 POSIX 函数,localtime_r 不可用;Linux/macOS 下 localtime_s 不存在。不能硬写一个版本,必须条件编译。
关键点:
- 包含
<ctime>和<chrono>,Windows 还需定义_CRT_SECURE_NO_WARNINGS(否则localtime_s报告 deprecated) - 用
#ifdef _WIN32区分平台,避免混用 -
std::tm初始化必须清零({}或= {}),否则未初始化字段(如tm_isdst)可能为随机值,影响夏令时计算
C++20 的替代方案:用 std::chrono::year_month_day
如果确定用 C++20,可以绕过 std::tm,用 std::chrono::floor<std::chrono::days>() 截断到天,再转成日历类型:
auto now = std::chrono::system_clock::now();
auto days = std::chrono::floor<std::chrono::days>(now);
std::chrono::year_month_day ymd {days};
std::ostringstream oss;
oss << ymd; // 输出形如 "2024-05-22"
std::string date_str = oss.str();
但要注意:year_month_day 表示的是“UTC 时间的日期”,不是本地时区。如果系统时区不是 UTC(绝大多数情况都不是),这会出错。正确做法是先转成本地日历:用 std::chrono::zoned_time 获取本地时区,再 floor:
- 需要
<chrono>和<date/date.h>(Howard Hinnant 的 date 库,或 C++20 标准库已内置部分功能) - 标准库中
zoned_time构造依赖current_zone(),某些旧 libc++ 实现不支持,编译可能失败 - 纯标准 C++20 方案目前仍不如传统
strftime兼容性强
本地日期这事,看似简单,其实卡在时区转换、线程安全、平台差异三处。别图省事跳过 _r/_s 后缀,也别默认 year_month_day 就是本地日期——它不是。


















