c++20推荐用std::chrono+std::format:先构造std::chrono::seconds,再duration_cast拆解为时分秒,用{:02}补零;c++17及以下直接整除取余,配合std::ostringstream与setfill/setw格式化。

用 std::chrono + std::format(C++20)最干净
如果你的编译器支持 C++20 且启用了 std::format(如 MSVC 19.3x、GCC 13+ 配合 -std=c++20 -lstdc++fs),这是目前最接近“优雅”的方案:类型安全、无手动除法、不依赖 C 风格函数。
常见错误是直接对原始整数秒调用 std::format,结果只能输出数字本身,无法自动拆解为时分秒。必须先转成 std::chrono::seconds,再借助 std::chrono::duration_cast 拆解。
- 用
std::chrono::seconds{total_sec}构造持续时间对象 - 分别 cast 到
std::chrono::hours、std::chrono::minutes、std::chrono::seconds,注意后两者要减去高位部分 -
std::format中用{:02}补零,避免写printf风格的格式串
int total_sec = 3665;
auto dur = std::chrono::seconds{total_sec};
auto h = std::chrono::duration_cast<std::chrono::hours>(dur);
auto m = std::chrono::duration_cast<std::chrono::minutes>(dur - h);
auto s = std::chrono::duration_cast<std::chrono::seconds>(dur - h - m);
std::string result = std::format("{:02}:{:02}:{:02}", h.count(), m.count(), s.count());
// → "01:01:05"
兼容 C++11/14/17:老老实实做整除和取余
绝大多数项目仍卡在 C++17 或更低版本,std::format 不可用,也别硬套 strftime(它要塞进 tm 结构体,得先转成日历时间,秒数超 86400 就溢出或错乱)。
直接算最稳:所有主流编译器对整数除法优化极好,性能毫无压力,逻辑也一目了然。
立即学习“C++免费学习笔记(深入)”;
- 小时:
total_sec / 3600 - 剩余秒数:
total_sec % 3600 - 分钟:
remaining / 60 - 秒:
remaining % 60 - 补零统一用
std::setfill('0') + std::setw(2),比手拼字符串或sprintf更安全
int total_sec = 3665;
int h = total_sec / 3600;
int m = (total_sec % 3600) / 60;
int s = total_sec % 60;
std::ostringstream oss;
oss << std::setfill('0') << std::setw(2) << h << ':'
<< std::setfill('0') << std::setw(2) << m << ':'
<< std::setfill('0') << std::setw(2) << s;
std::string result = oss.str(); // → "01:01:05"
别踩 localtime + strftime 的坑
网上很多示例用 localtime(&time_t{sec}) 再 strftime,这在秒数小于 86400(一天)时看似能跑,但本质是误用:
-
time_t表示的是自 epoch 起的秒数,不是“持续时间”。传入3665实际对应 1970-01-01 01:01:05 UTC,时区一变就错 - 超过 86400 秒(比如 90000),
localtime会解析成“1970-01-02 01:00:00”,小时变成 01 而非期望的 25 - 跨夏令时、闰秒、甚至某些嵌入式平台的
time_t实现,会让结果不可预测
除非你真想把秒数解释为某个日历时刻,否则永远别碰这套组合。
输出到 std::cout 还是 std::string?看场景选流操作
如果只是临时调试打印,直接用 std::cout 配合 std::setfill 和 std::setw 最轻量;如果要拼进日志、返回给上层或做进一步处理,必须构造 std::string。
容易忽略的一点:std::setfill 和 std::setw 是流状态,会影响后续所有输出。如果写成全局 std::cout ,后面打个普通数字也可能被补零。
- 调试用:用
std::ostringstream隔离状态,或每次用std::cout 单次生效 - 生产用:封装成函数,返回
std::string,避免副作用泄漏 - 性能敏感场景(高频调用):预分配
std::array<char></char>手动写入,跳过流对象开销(但多数情况没必要)
时分秒拆解本身没玄机,真正容易翻车的是混淆“持续时间”和“日历时间”,以及忽略流操作符的状态污染。选对抽象层级,比追求某一行“炫技”代码重要得多。










