做开发的人应该都见过这种数字:1719484800、1686240000。一串看起来没什么意义的整数,其实它代表的是一个精确的时间点。这就是 Unix 时间戳(Unix Timestamp),也叫 Epoch Time。几乎所有编程语言和数据库都用它来存储和传递时间。

什么是 Unix 时间戳

Unix 时间戳的定义很简单:从 1970 年 1 月 1 日 00:00:00 UTC 到现在经过了多少秒(或毫秒)。这个起始时间点叫作 "Unix Epoch",是 Unix 系统诞生的时间,被选作为时间的零点。

比如 1719484800 这个数字,换算成人类能看懂的时间就是 2024 年 6 月 27 日 16:00:00 UTC(北京时间是当天午夜 0 点)。

用整数表示时间有几个好处:

  • 存储空间小:一个 64 位整数就够了
  • 比较和排序方便:数字大的就是更晚的时间
  • 跨语言通用:不管用什么编程语言,同一个时间戳代表的时刻是一致的
  • 计算简单:两个时间戳相减就能得到时间差

秒级和毫秒级时间戳有什么区别

这是新手最常搞混的地方。

秒级时间戳是 10 位数字(当前大概是 17 亿左右),表示从 Epoch 到现在经过的整秒数。毫秒级时间戳是 13 位数字,表示经过的毫秒数。两者之间差 1000 倍。

快速判断一个时间戳是秒还是毫秒的方法:数位数

  • 10 位数字 = 秒级(范围大约到 2286 年才溢出)
  • 13 位数字 = 毫秒级(范围大概到 2286 年溢出)
  • 如果位数在中间,可能是其他精度或者格式不对

不同系统和语言用的精度不一样。JavaScript 的 Date.now() 返回的是毫秒级;Python 的 time.time() 返回的是浮点数秒(带小数的);Java 的 System.currentTimeMillis() 是毫秒级;MySQL 的 UNIX_TIMESTAMP() 函数返回的是秒级;大多数 REST API 的接口文档会明确说明用的是哪种精度,开发之前一定要确认。

把秒级转成毫秒级就乘以 1000,反过来除以 1000 取整就行。但要注意精度丢失——毫秒级转成秒级再转回来,最后三位就变成 000 了。

各语言怎么获取当前时间戳

下面列出几种常见语言获取当前时间戳的写法,方便查用。

JavaScript / TypeScript

// 毫秒级
const timestampMs = Date.now();
// 或者
const timestampMs2 = new Date().getTime();

// 秒级
const timestampS = Math.floor(Date.now() / 1000);

Python

import time

# 秒级(浮点数)
timestamp_s = time.time()
# 秒级(整数)
timestamp_s_int = int(time.time())

# 毫秒级
timestamp_ms = int(time.time() * 1000)

Java

// 毫秒级
long timestampMs = System.currentTimeMillis();

// 秒级
long timestampS = System.currentTimeMillis() / 1000L;

// Java 8+ 还可以用 Instant
long timestampMs2 = Instant.now().toEpochMilli();

Go

package main

import (
    "fmt"
    "time"
)

func main() {
    // 秒级
    timestampS := time.Now().Unix()
    // 毫秒级
    timestampMs := time.Now().UnixMilli()
    // 纳秒级
    timestampNs := time.Now().UnixNano()

    fmt.Println(timestampS)
}

PHP

// 秒级
$timestampS = time();

// 毫秒级
$timestampMs = round(microtime(true) * 1000);

这些代码片段随时可以用Unix 时间戳转换工具验证结果对不对。

时间戳的时区怎么处理

Unix 时间戳本身不带时区信息,它永远是 UTC 时间的偏移量。但转换成可读字符串的时候就必须考虑时区了。

同一个时间戳 1719484800:

  • 在 UTC 时区显示为:2024-06-27 16:00:00
  • 在北京时间 (UTC+8) 显示为:2024-06-28 00:00:00
  • 在纽约时间 (UTC-4/-5) 显示为:2024-06-27 12:00:00

所以当你发现前端显示的时间和后端存的时间差几个小时,大概率是时区没处理对。常见的问题包括:

  • 后端存的是 UTC 时间戳,前端直接 new Date(timestamp) 转换后按本地时区显示了,看起来跟预期不一致
  • 数据库里存的是时间戳,但是查询的时候用了带时区的函数导致结果偏移
  • 前后端约定好用秒级,结果一方给了毫秒级,时间差了 1000 倍(这个比时区问题还离谱,但确实经常发生)

最佳实践是在服务端统一用 UTC 存储,展示的时候根据用户的时区偏好再做转换。不要在数据层混入时区逻辑。

2038 年问题

如果你听说过 "Y2K"(千年虫)问题,那 2038 问题就是它的 32 位版。

早期的 Unix 系统用 32 位有符号整数来存时间戳(秒级)。32 位有符号整数的最大值是 2147483647,对应的时间是 2038 年 1 月 19 日 03:14:07 UTC。过了这一秒之后,整数溢出,时间戳会变成负数,系统可能认为时间回到了 1901 年。

这个问题影响的是还在用 32 位 time_t 类型的老系统。现代的 64 位系统不受影响(64 位能表示到公元 2920 亿年左右)。但如果你的代码跑在嵌入式设备上、或者跟一些遗留系统对接,还是要留意一下。

解决方法也很直接:把时间相关的变量类型升级到 64 位(比如 C 语言里用 time64_t 替代 time_t)。大部分主流操作系统和编程语言的最新版本已经解决了这个问题。

实际开发中时间戳有哪些坑

除了上面说的几点,还有一些容易踩的小坑:

  • 精度不一致:前端 JavaScript 用毫秒,后端 Python 用秒,传参的时候忘了乘除 1000,查 bug 查半天
  • 闰秒:Unix 时间戳不计算闰秒,所以它和实际的原子时会有几秒的差异,普通业务不用管,但对时间敏感的系统要了解
  • 数据库存储:MySQL 的 TIMESTAMP 类型有范围限制(1970 到 2038,32 位系统上),超出范围会报错或变成 0000-00-00。建议用 DATETIME 或 BIGINT 存储时间戳
  • JSON 序列化:时间戳是数字,JSON 传输没问题。但有些框架会把 Date 对象自动序列化成字符串,反序列化回来的时候要注意格式

常见问题

时间戳能表示未来多久的时间?

64 位系统上,秒级时间戳理论上可以表示到公元 2920 亿年,基本不用担心。但 32 位系统上,最大只能到 2038 年 1 月 19 日。另外 MySQL 的 TIMESTAMP 类型范围是 1970-2038,超出范围会报错,建议用 DATETIME 替代。

怎么快速把时间戳转成可读时间?

用 速工具 的时间戳转换工具,粘贴数字直接出结果。命令行下,Linux 可以用 date -d @1719484800,Mac 上用 date -r 1719484800。JavaScript 里 new Date(1719484800 * 1000) 就行(注意乘 1000 转毫秒)。

不同数据库的时间戳格式一样吗?

不一样。MySQL 的 UNIX_TIMESTAMP() 返回秒级整数,MongoDB 的 ISODate() 存的是 ISO 8601 字符串,PostgreSQL 的 EXTRACT(EPOCH FROM ...) 返回浮点数秒。跨数据库操作时一定要确认格式,避免差 1000 倍的问题。

小结

Unix 时间戳就是一个从 1970-01-01 开始计数的整数,用来表示时间点。记住几个关键点:10 位是秒级、13 位是毫秒级;它不带时区信息;2038 年问题只影响 32 位系统;不同语言的 API 精度不一样,跨语言调用时要统一。日常开发中遇到时间相关的问题,时间戳转换工具可以帮你快速在可读时间和时间戳之间互转,排查问题时很实用。

Unix 时间戳工具

打开时间戳转换工具 →