在前后端接口通信、保存程序日志或数据库存储时,直接传递 "2026-10-24 15:30:00" 这样的日期字符串不太方便,因为不同国家和地区还会涉及时区换算与格式差异。
为了统一标准,实际开发中更通用的做法是使用 “时间戳(Timestamp)”。所谓的时间戳,指的就是从世界标准时间 1970 年 1 月 1 日 00:00:00 UTC 到某个时刻一共经过了多少秒或多少毫秒。在计算机领域,这个统一起算点在英文中称为 “epoch(纪元)”。
获取秒级与毫秒级时间戳
在 C++ 中,我们可以借助 chrono 库中的 std::chrono::system_clock::now() 获取当前的系统时间点,再调用时间点对象的 time_since_epoch() 成员函数算出时间戳。
语法:
auto dur = 时间点.time_since_epoch();
long long ts = std::chrono::duration_cast<目标单位>(dur).count();说明:
time_since_epoch 从英文意思上就很好理解,表示 “自纪元以来经过的时间”。
初学的小伙伴最容易忽略的一点是:time_since_epoch() 返回的仍然是一个 duration 时间段对象,而不是直接返回一个整数。我们需要先通过 duration_cast 将其换算成秒(seconds)或毫秒(milliseconds),最后再调用 count() 成员函数,才能拿到纯整数形式的时间戳。
1. 获取 10 位秒级时间戳
当我们把转换的目标单位指定为 std::chrono::seconds 时,拿到的就是常用的 10 位数秒级时间戳。下面我们来看一个例子。
示例 1:获取秒级时间戳
#include <chrono>
#include <iostream>
int main() {
// 获取当前时间点与自纪元以来的时间段
auto now = std::chrono::system_clock::now();
auto d = now.time_since_epoch();
// 换算为总秒数并取出整数值
auto s = std::chrono::duration_cast<std::chrono::seconds>(d);
long long tsSec = s.count();
std::cout << "秒级时间戳:" << tsSec << '\n';
return 0;
}运行结果如下。
秒级时间戳:1792827000分析:
now.time_since_epoch() 先拿到自 1970 年元旦零点以来经过的时间段 d。接着我们通过 duration_cast<std::chrono::seconds>(d) 将其换算为秒级时间段 s,最后调用 s.count() 取出了整数值 1792827000,每过去 1 秒这个 10 位整数就会加 1。
需要注意的是,now.time_since_epoch() 返回的是时间段对象而非普通整数,不能直接把它赋值给 long long 变量。下面这种漏掉转换与 .count() 的写法就是错误的,编译时会直接报错。
// 错误方式
long long badTs = now.time_since_epoch();2. 获取 13 位毫秒级时间戳
在实际开发中,除了 10 位的秒级时间戳之外,13 位的毫秒级时间戳也经常用到。两者的计算步骤完全一致,只是把 duration_cast 的目标单位换成 milliseconds。下面我们在函数内引入局部命名空间 std::chrono,同时打印秒级和毫秒级时间戳来做对比。
示例 2:对比秒级和毫秒级时间戳
#include <chrono>
#include <iostream>
int main() {
using namespace std::chrono;
// 获取同一个当前时间点
auto now = system_clock::now();
auto dur = now.time_since_epoch();
// 分别换算为秒级与毫秒级时间戳
long long tsSec = duration_cast<seconds>(dur).count();
long long tsMs = duration_cast<milliseconds>(dur).count();
std::cout << "秒级时间戳(s):" << tsSec << '\n';
std::cout << "毫秒级时间戳(ms):" << tsMs << '\n';
return 0;
}运行结果如下。
秒级时间戳(s):1792827000
毫秒级时间戳(ms):1792827000447分析:
从运行结果可以看出,毫秒级时间戳 tsMs 比秒级时间戳 tsSec 多出了末尾的 3 位数字 447,代表当前这一秒内已经走过的 447 毫秒。当换算成整数秒时,不足 1 秒的 447 毫秒会直接截断舍去。
如果平时只需要单独获取一个时间戳,我们也可以把获取时间点与 time_since_epoch() 连起来写。对于上面获取毫秒级时间戳的代码来说,下面 2 种方式是等价的。
// 方式 1
auto now = system_clock::now();
auto dur = now.time_since_epoch();
long long tsMs = duration_cast<milliseconds>(dur).count();
// 方式 2
auto dur = system_clock::now().time_since_epoch();
long long tsMs = duration_cast<milliseconds>(dur).count();注意
统一使用 long long 保存时间戳:根据不同数据类型的取值范围,普通的 32 位 int 最大只能表示约 21 亿(10 位数字),装不下 13 位的毫秒级时间戳。因此保存时间戳时,我们应该统一使用 long long 类型,避免发生数据溢出。
确认接口要求的单位是秒还是毫秒:整数时间戳本身不带单位信息。我们与前后端接口对接时,要先确认对方要求传 10 位秒级还是 13 位毫秒级,避免时间相差 1000 倍。
将时间戳还原为日期时间
学会了把当前时间转换成整数时间戳之后,反过来,如果程序从配置文件或网络接口收到了一个整数时间戳(比如 1792827000),我们应该怎么把它还原成可读的日期字符串呢?
其实把前面的步骤倒过来就可以了:我们先使用这个整数构造对应单位的 duration 时间段,再传入该时间段构造 system_clock::time_point 时间点,最后按照前面获取当前时间时的步骤格式化输出。
语法:
std::chrono::时间单位 dur{时间戳整数};
std::chrono::system_clock::time_point tp{dur};说明:
如果收到的是 10 位秒级时间戳,时间单位就使用 std::chrono::seconds;如果收到的是 13 位毫秒级时间戳,时间单位就换成 std::chrono::milliseconds。
示例 3:将时间戳还原为日期时间
#include <chrono>
#include <ctime>
#include <iomanip>
#include <iostream>
int main() {
using namespace std::chrono;
// 待还原的 10 位秒级时间戳
long long tsSec = 1792827000;
// 构造时间段并还原为系统时间点
seconds dur{tsSec};
system_clock::time_point tp{dur};
// 转为本地时间并格式化打印
std::time_t t = system_clock::to_time_t(tp);
std::tm* p = std::localtime(&t);
std::cout << "原始时间戳:" << tsSec << '\n';
std::cout << "还原后时间:";
std::cout << std::put_time(p, "%Y-%m-%d %H:%M:%S") << '\n';
return 0;
}运行结果如下。
原始时间戳:1792827000
还原后时间:2026-10-24 15:30:00分析:
seconds dur{tsSec}; 表示自 1970 年元旦零点以来经过了 1792827000 秒,接着通过 system_clock::time_point tp{dur}; 就还原出了对应的系统时间点。需要注意,std::localtime() 拆解出的本地日期与电脑当前所在的时区有关,上面的运行结果对应北京时间(UTC+8)。
熟悉之后,小伙伴们也可以把构造时间段和构造时间点合写成一行:
// 还原 10 位秒级时间戳
system_clock::time_point tp1{seconds{1792827000}};
// 还原 13 位毫秒级时间戳
system_clock::time_point tp2{milliseconds{1792827000447}};对于这种两层花括号嵌套的写法,我们可以从内往外看:内层的 seconds{1792827000} 先把整数包装成秒级时间段,外层再用这个时间段初始化时间点 tp1。还原时间戳时千万不要把 13 位的毫秒数误传给 seconds{...},否则算出来的年份就会变成几万年以后。
常见问题
1. 在一些旧代码里看到 std::time(nullptr),它和 chrono 获取时间戳有什么区别?
<ctime> 头文件中的 std::time(nullptr) 沿用了 C 语言的传统写法,调用后会直接返回当前的 10 位秒级时间戳,写起来比较简短。
不过 std::time() 最高只能精确到 “秒”,无法直接获取 13 位的毫秒级时间戳。在实际开发中,如果只需要简单的秒级时间戳,两种写法都可以;如果需要毫秒、微秒精度或配合 <chrono> 做时间换算,更推荐使用这一节介绍的方式。
