c++oding="utf-8" ?>
应先转换小时再补前缀:0→12 am,12→12 pm,1–11→hour am,13–23→(hour-12) pm;分钟用std::setfill('0')补零。

如何用 std::ostringstream 安全拼接 12 小时制字符串
直接用 std::ostringstream 拼接比手动格式化更可靠,避免 printf 风格的缓冲区风险和 locale 依赖问题。关键在于先做小时转换再补前缀,而不是依赖 %I 这类 C 风格格式符(C++ 标准库不支持)。
常见错误是把 0 小时(即 00:xx)当成午夜处理成 12:xx AM,但把 12 小时(即 12:xx)误判为中午却没加 PM;还有人忽略分钟补零,导致 "9:5 AM" 这种非法输出。
-
hour为0或12时需特殊判断:0 → 12 AM,12 → 12 PM,其余1–11 → hour AM,13–23 → (hour - 12) PM - 分钟必须用
std::setfill('0') 补零,否则 <code>9:05会变成9:5 - 不要用
strftime:它依赖 C locale,且在 Windows 上对中文 locale 可能输出乱码或崩溃
手写转换函数时怎么处理边界值
24 小时制合法范围是 0–23 小时、0–59 分钟。超出范围的输入(如 24:00 或 12:60)不应静默修正,而应由调用方负责校验——转换函数只做纯映射。
容易被忽略的是 00:00 和 12:00 的语义差异:00:00 是午夜(12:00 AM),12:00 是正午(12:00 PM)。错把两者都转成 12:00 不加 AM/PM,就失去区分能力。
-
hour % 12不能直接用:因为0 % 12 == 0,12 % 12 == 0,二者结果相同,必须单独分支判断 - 推荐写法:
display_hour = (hour == 0 || hour == 12) ? 12 : hour % 12; period = (hour —— 注意是 <code>,不是 <code>,逻辑更清晰
要不要用 std::format(C++20)
如果你项目已启用 C++20 且编译器支持(GCC 13+、Clang 15+、MSVC 19.30+),std::format 是最简方案,但要注意它目前不支持自定义 AM/PM 字符串(固定为英文),也无法控制大小写。
例如 std::format("{:%I:%M %p}", tp) 会输出 "12:34 AM",但无法改成 "12:34 am" 或中文 "上午"。若需本地化,仍得回退到手写逻辑 + std::locale 查询。
- 使用前确认
__cpp_lib_format宏是否定义,某些旧版 STL(如 libstdc++ 12)虽支持语法但%p实现有 bug - 测试时务必覆盖
0、12、13三个关键小时值 - 不建议在嵌入式或资源受限环境用
std::format:它依赖动态内存分配,开销明显高于ostringstream
为什么不用 ctime + strftime
虽然 strftime("%I:%M %p", ...) 看似一行解决,但它严重依赖全局 locale 设置。一旦程序中其他地方调用了 setlocale(LC_TIME, "..."),就可能让转换结果突变为日文或中文格式(如 "午前09:05"),而你的代码里根本没显式设置 locale。
更隐蔽的问题是线程安全:C 标准库的 strftime 使用内部静态缓冲区,在多线程下若未加锁,可能返回截断或混乱字符串。
- 即使你写了
setlocale(LC_TIME, "C"),也不能保证其他第三方库没改 locale —— 它是进程级全局状态 -
strftime对非法时间(如25:00)行为未定义,可能返回空字符串或崩溃 - 跨平台兼容性差:Windows 的
%p在某些 locale 下输出"am"/"pm"全小写,Linux 可能大写,无法统一
实际转换逻辑并不复杂,但小时模 12 的边界处理和 AM/PM 判定稍不留神就会出错。真正麻烦的是后续需求扩展——比如支持 24 小时制带秒、支持毫秒、支持不带前导零的格式、支持本地化缩写……这些都会让简单函数迅速膨胀。不如一开始就把小时转换、周期判定、格式拼接三部分拆开,方便未来替换任意一环。
C++免费学习笔记(深入):立即使用
在学习笔记中,你将探索 C++ 的入门与实战技巧!











