快速判断:10 位还是 13 位?
在处理时间戳时,最常遇到的问题就是:拿到一串数字,它到底是秒级还是毫秒级?其实判断规则非常简单:
- 10 位数字 = 秒级时间戳(Unix Timestamp)
- 13 位数字 = 毫秒级时间戳(Millisecond Timestamp)
以下是同一个时间点,以两种格式呈现的对照表:
| 格式 | 位数 | 示例值 | 对应时间 |
|---|---|---|---|
| 秒级(seconds) | 10 位 | 1700000000 |
2023-11-14 22:13:20 UTC |
| 毫秒级(milliseconds) | 13 位 | 1700000000000 |
2023-11-14 22:13:20.000 UTC |
简单来说,毫秒级时间戳就是秒级时间戳后面多了三个零(或三位毫秒数值),精度从「秒」提升到了「千分之一秒」。
为什么会有两种格式?
历史渊源
最早的 Unix 系统在 1970 年代诞生时,以「秒」作为时间的最小计量单位已经足够精确。当时的硬件性能有限,使用 32 位整数存储秒级时间戳既省空间又够用。这就是经典的 Unix Timestamp——一个 10 位的整数。
毫秒级的崛起
随着网络应用与实时系统的发展,「秒」的精度逐渐不敷使用。浏览器需要精确计时动画帧、数据库需要排序同一秒内的多笔操作、分布式系统需要更细腻的时间顺序。于是,毫秒级时间戳(13 位)应运而生。
各编程语言的默认单位
| 语言 / 环境 | 默认单位 | 获取方式 |
|---|---|---|
| JavaScript | 毫秒(ms) | Date.now() |
| Java | 毫秒(ms) | System.currentTimeMillis() |
| C# / .NET | 毫秒(ms) | DateTimeOffset.UtcNow.ToUnixTimeMilliseconds() |
| Python | 秒(s) | time.time() |
| PHP | 秒(s) | time() |
| Go | 秒(s) | time.Now().Unix() |
| Unix / Linux Shell | 秒(s) | date +%s |
| MySQL | 秒(s) | UNIX_TIMESTAMP() |
正因为不同语言的默认值不同,才会经常出现秒与毫秒混淆的问题。当前端用 JavaScript 产生毫秒级时间戳传给后端的 Python 或 PHP 处理时,如果忘记转换,就会得到完全错误的日期。
互相转换
秒与毫秒之间的转换公式非常简单:
- 秒 → 毫秒:
milliseconds = seconds * 1000 - 毫秒 → 秒:
seconds = Math.floor(milliseconds / 1000)
JavaScript
// 秒 → 毫秒
const seconds = 1700000000;
const milliseconds = seconds * 1000;
// 结果: 1700000000000
// 毫秒 → 秒
const ms = 1700000000000;
const sec = Math.floor(ms / 1000);
// 结果: 1700000000
// 从 Date 对象获取两种格式
const now = new Date();
const nowMs = now.getTime(); // 毫秒(13 位)
const nowSec = Math.floor(nowMs / 1000); // 秒(10 位)
Python
import time
# 秒 → 毫秒
seconds = 1700000000
milliseconds = seconds * 1000
# 结果: 1700000000000
# 毫秒 → 秒
ms = 1700000000000
sec = ms // 1000
# 结果: 1700000000
# 获取当前时间的两种格式
now_sec = int(time.time()) # 秒(10 位)
now_ms = int(time.time() * 1000) # 毫秒(13 位)
PHP
// 秒 → 毫秒
$seconds = 1700000000;
$milliseconds = $seconds * 1000;
// 结果: 1700000000000
// 毫秒 → 秒
$ms = 1700000000000;
$sec = intdiv($ms, 1000);
// 结果: 1700000000
// 获取当前时间的两种格式
$nowSec = time(); // 秒(10 位)
$nowMs = (int)(microtime(true) * 1000); // 毫秒(13 位)
自动检测技巧
在实际开发中,经常需要编写一个函数来自动判断输入的时间戳是秒级还是毫秒级。最常用的方法是阈值判断法:
核心逻辑
因为秒级时间戳目前最多 10 位,其最大值为 9999999999(对应 2286 年 11 月 20 日),所以:
- 数值 ≤ 9999999999 → 判定为秒级
- 数值 > 9999999999 → 判定为毫秒级
function detectTimestamp(value) {
const num = Number(value);
if (isNaN(num) || num < 0) return null;
if (num > 9999999999) {
// 毫秒级
return {
type: 'milliseconds',
ms: num,
seconds: Math.floor(num / 1000)
};
} else {
// 秒级
return {
type: 'seconds',
seconds: num,
ms: num * 1000
};
}
}
边界情况注意事项
- 非常早期的毫秒时间戳:例如
1000000000(10 位),既可以是 2001-09-09 的秒级时间戳,也可以是 1970-01-12 的毫秒级时间戳。此时阈值法会将其判定为秒级,在绝大多数情况下这是正确的。 - 微秒与纳秒时间戳:16 位(微秒)或 19 位(纳秒)的数值需要额外处理,不在秒/毫秒的判断范围内。
- 负数时间戳:表示 1970 年以前的时间,阈值法不适用,需要根据业务逻辑另行处理。
- 字符串前导零:如
"0170000000"实际上只有 9 位有效数字,应先转为数值再判断。
常见混淆情境
在实际开发中,秒与毫秒的混用是一个非常常见的 Bug 来源。以下是几个典型案例:
案例一:API 响应格式不一致
某个系统的用户 API 返回秒级时间戳,但订单 API 返回毫秒级时间戳。前端统一用 new Date(timestamp) 处理,结果用户注册时间显示正确,但订单创建时间却跑到了公元 55000 年。
// 错误:把秒级时间戳直接传给 Date 构造函数
new Date(1700000000)
// 结果: 1970-01-20(错误!差了 50 多年)
// 正确:秒级必须先乘以 1000
new Date(1700000000 * 1000)
// 结果: 2023-11-14 22:13:20(正确)
案例二:数据库字段类型不匹配
后端 Python 用 int(time.time()) 产生秒级时间戳存入数据库,但另一个 Java 服务用 System.currentTimeMillis() 写入同一张表。查询排序时,毫秒级的数据永远排在最后面,因为数值大了 1000 倍。
案例三:日志时间戳解析错误
Elasticsearch 的 @timestamp 字段默认期望毫秒级时间戳。如果你送进秒级的数值,所有日志都会被归档到 1970 年 1 月,导致 Kibana 面板上完全看不到数据。
案例四:缓存过期时间计算错误
在 Redis 中设置 key 的过期时间,EXPIRE 指令使用秒数,PEXPIRE 使用毫秒数。如果误把毫秒数传给 EXPIRE,原本要缓存 1 小时的数据,变成缓存了 3,600,000 秒(约 41 天)。
如何避免混淆
- 在 API 文档中明确标注时间戳的单位(秒或毫秒)。
- 变量命名加入后缀:
createdAtSec或createdAtMs。 - 统一团队规范,所有接口一律使用同一种格式。
- 在接收端加入自动检测逻辑,作为最后一道防线。
快速参考:同一时刻的不同格式
| 日期时间(UTC) | 秒级(10 位) | 毫秒级(13 位) |
|---|---|---|
| 1970-01-01 00:00:00 | 0 |
0 |
| 2000-01-01 00:00:00 | 946684800 |
946684800000 |
| 2020-01-01 00:00:00 | 1577836800 |
1577836800000 |
| 2025-01-01 00:00:00 | 1735689600 |
1735689600000 |
| 2030-01-01 00:00:00 | 1893456000 |
1893456000000 |
| 2038-01-19 03:14:07 | 2147483647 |
2147483647000 |
自动检测工具
输入一个数字,自动判断它是秒级还是毫秒级时间戳,并显示两种格式的转换结果。
常见问题 FAQ
如何快速分辨 10 位和 13 位时间戳?
最简单的方法就是数位数:10 位是秒级,13 位是毫秒级。在程序中则可以用阈值判断——如果数值大于 9,999,999,999(约 100 亿),就是毫秒级。目前秒级时间戳约为 17 亿,而毫秒级约为 1.7 万亿,两者在数量级上差异非常明显。
混用秒和毫秒会造成什么问题?
最常见的后果是日期显示完全错误。把秒级时间戳当作毫秒使用,日期会回到 1970 年 1 月;把毫秒级时间戳当作秒使用,日期会跳到公元数万年。这类 Bug 在前后端对接、微服务之间的数据传递、以及日志系统中特别容易发生。严重时可能导致数据排序错误、缓存失效时间异常、甚至交易记录时间紊乱。
微秒和纳秒时间戳又是什么?
除了秒(10 位)和毫秒(13 位),还有更高精度的格式:微秒(microseconds,16 位,1 秒 = 1,000,000 微秒)和纳秒(nanoseconds,19 位,1 秒 = 1,000,000,000 纳秒)。Go 语言的 time.Now().UnixNano() 返回的就是纳秒级时间戳。这些高精度格式常用于性能测量、高频交易系统等对时间精度要求极高的场景。
哪些编程语言默认使用毫秒?
JavaScript、Java 和 C#(.NET)默认使用毫秒级时间戳。JavaScript 的 Date.now()、Java 的 System.currentTimeMillis()、C# 的 DateTimeOffset.UtcNow.ToUnixTimeMilliseconds() 都返回 13 位毫秒数。而 Python、PHP、Go、Ruby、Unix Shell 和 MySQL 则默认使用秒级时间戳。使用跨语言的项目时,务必确认双方使用的单位是否一致。