C++ 获取时间戳

在前后端接口通信、保存程序日志或数据库存储时,直接传递 "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> 做时间换算,更推荐使用这一节介绍的方式。

给站长反馈

绿叶网正在不断完善中,小伙伴们如果发现任何问题,还望多多给站长反馈,谢谢!

邮箱:lvyenet@vip.qq.com

「绿叶网」服务号
绿叶网服务号放大
关注服务号,微信也能看教程。
绿叶网服务号