最稳的做法是手动拼接中文字符:先用线程安全函数获取年月日整数,再用 std::to_string 拼接“年”“月”“日”,完全避开 locale 和宽字符依赖,跨平台兼容 gcc/clang/msvc。

用 std::put_time 格式化时间戳为中文日期(如“2023年10月1日”)
直接用 std::put_time 无法输出“年/月/日”这类中文字符,它只处理格式符(如 %Y、%m),不负责本地化字符串。想得到“2023年10月1日”,必须手动拼接或借助宽字符 + 本地化 facet,但后者在各平台行为不一致(尤其是 Windows MSVC 对 std::time_put 的中文支持极弱)。
最稳的实操方式是:先用 std::strftime 或 std::put_time 提取数字部分,再用 C++ 字符串拼接中文单位。
-
std::time_t时间戳转std::tm时,务必用std::localtime_r(Linux/macOS)或localtime_s(Windows),避免std::localtime的线程安全问题 - 月份和日期要手动去掉前导零(
%-m和%-d在 Linux 可用,但 Windows 不支持;稳妥做法是用%m/%d后调std::stoi转整数再转回字符串) - 别依赖
setlocale(LC_TIME, "zh_CN.UTF-8")试图让%B输出“十月”——它在多数发行版上根本不管用,且 Windows 的 locale 名称又不一样(如"Chinese_China.936"),跨平台维护成本高
手拼字符串:安全、跨平台、无 locale 依赖
这是目前最推荐的做法。核心逻辑是:取 %Y、%m、%d 的纯数字结果,转成整数后按需格式化,再插入中文字符。
std::string format_chinese_date(std::time_t ts) {
std::tm tm_buf {};
#ifdef _WIN32
localtime_s(&tm_buf, &ts);
#else
localtime_r(&ts, &tm_buf);
#endif
int year = tm_buf.tm_year + 1900;
int month = tm_buf.tm_mon + 1; // tm_mon 是 0-based
int day = tm_buf.tm_mday;
return std::to_string(year) + "年" +
std::to_string(month) + "月" +
std::to_string(day) + "日";
}
- 完全避开 locale 和宽字符,所有主流编译器(GCC、Clang、MSVC)都能直接编译运行
- 不需要额外链接 ICU 或 Boost,也不依赖系统 locale 文件是否存在
- 如果要求补零(如“01日”),把
std::to_string(day)换成(day
为什么不用 std::format(C++20)?
std::format 目前(GCC 13 / Clang 16 / MSVC 19.3x)对日期格式化仅支持 {:%Y-%m-%d} 这类 POSIX 风格,不支持嵌入中文字符,也无法自定义分隔符文本。你写 "{:%Y}年{%m}月{%d}日" 会编译失败——std::format 不允许在格式字符串中混用字面量和日期说明符。
- 即使未来标准支持,也要等各家编译器实现完整,短期内不可靠
- 当前唯一能用的日期格式化能力仍局限在
%开头的 C 风格说明符内,本质和std::put_time一致 - 所以 C++20 并未降低“手拼字符串”的必要性,反而让开发者误以为有更高级方案可用
注意 tm 的时区与夏令时陷阱
std::localtime 系列函数返回的是本地时区时间,但“本地”取决于程序启动时的环境变量(如 TZ)或系统设置。如果你传入的时间戳本意是 UTC,却用了 localtime,结果会偏移。
- 确认输入时间戳的语义:是 Unix timestamp(UTC)?还是已经按本地时区调整过的值?
- 若需严格按 UTC 输出“2023年10月1日”,应改用
std::gmtime_r/gmtime_s,然后同样手拼字符串 -
tm.tm_isdst字段在夏令时切换日附近可能为 -1(未知),但只要不依赖它做逻辑判断,对手拼日期无影响
C++免费学习笔记(深入):立即使用
在学习笔记中,你将探索 C++ 的入门与实战技巧!











