日期显示 1970 年,或者年份跑到了五万多年后
这两个症状,来自同一个 bug。
你的代码里有个时间戳,传给了某个函数,结果要么输出 1970-01-20,要么输出某个几万年后的日期。翻来覆去找不到哪里写错了,但其实原因就一个字:位。
时间戳有两种精度——秒和毫秒——差了 1000 倍。两者混用,日期就会错得离谱。
为什么 JavaScript 用毫秒,其他语言用秒?
Unix 标准定义时间戳是"从 1970-01-01 00:00:00 UTC 到现在经过的秒数",这是历史标准,Python、PHP、Go、数据库基本都遵循。
JavaScript 是个例外。Brendan Eich 在设计 JavaScript 的 Date 对象时,选择了毫秒作为单位,原因大概是当时认为毫秒精度对前端更合适。这个决定定下来就改不了了,现在整个前端生态都是毫秒。
所以现状是:
| 语言/系统 | 默认精度 | 典型位数 |
|---|---|---|
| Unix / Linux | 秒 | 10位 |
Python time.time() |
秒(浮点) | 10位整数部分 |
PHP time() |
秒 | 10位 |
Go time.Now().Unix() |
秒 | 10位 |
MySQL UNIX_TIMESTAMP() |
秒 | 10位 |
JavaScript Date.now() |
毫秒 | 13位 |
Java System.currentTimeMillis() |
毫秒 | 13位 |
看位数是最快的判断方式:10位是秒,13位是毫秒。
四种混用场景和对应修复
场景1:把秒传给了 JavaScript new Date()
// 后端返回了秒级时间戳
const ts = 1745000000; // 10位,秒
// 错误写法:new Date() 期望毫秒
const date = new Date(ts);
console.log(date.toISOString());
// 输出: "1970-01-20T20:06:40.000Z" ← 1970年!
// 正确写法:乘以1000
const date = new Date(ts * 1000);
console.log(date.toISOString());
// 输出: "2026-04-11T02:13:20.000Z" ← 正确
场景2:把毫秒传给了 Python datetime.fromtimestamp()
from datetime import datetime
ts = 1745000000000 # 13位,毫秒(比如从前端拿到的)
# 错误写法
dt = datetime.fromtimestamp(ts)
# ValueError: year 55916 is out of range ← 五万多年后!
# 正确写法:除以1000
dt = datetime.fromtimestamp(ts / 1000)
print(dt) # 2026-04-11 02:13:20
场景3:前端发给后端的时间戳没转换
这是最容易被忽视的场景,因为前后端各自测试都正常,联调才出问题。
// 前端:用 Date.now() 获取当前时间戳
const payload = {
created_at: Date.now() // 1745000000000,毫秒
};
fetch('/api/save', {
method: 'POST',
body: JSON.stringify(payload)
});
# 后端 Python:以为收到的是秒
from datetime import datetime
data = request.json
ts = data['created_at'] # 实际是 1745000000000
dt = datetime.fromtimestamp(ts) # 报错:year out of range
# 修复方案1:前端转成秒再发
# JavaScript: Math.floor(Date.now() / 1000)
# 修复方案2:后端判断位数自动处理
if ts > 1e10: # 超过100亿,说明是毫秒
ts = ts / 1000
dt = datetime.fromtimestamp(ts)
场景4:数据库存的是秒,Node.js 读出来当毫秒用
// 从数据库读出来的是秒
const row = await db.query('SELECT created_at FROM users WHERE id = 1');
const ts = row.created_at; // 1745000000,秒
// 直接用,结果显示 1970 年
const date = new Date(ts); // 错!
// 正确
const date = new Date(ts * 1000);
怎么判断手上的时间戳是秒还是毫秒?
方法1:看位数
2026年的秒级时间戳是10位(1700000000 到 1800000000之间),毫秒是13位。这个规律在2001年之后到2286年之前都成立。
方法2:写一个检测函数
function detectTimestamp(ts) {
if (ts > 1e12) return 'milliseconds';
if (ts > 1e9) return 'seconds';
return 'unknown';
}
// 统一转成毫秒给 Date 用
function toDate(ts) {
return new Date(ts > 1e12 ? ts : ts * 1000);
}
def detect_timestamp(ts):
if ts > 1e12:
return 'milliseconds'
if ts > 1e9:
return 'seconds'
return 'unknown'
def to_datetime(ts):
from datetime import datetime, timezone
if ts > 1e12:
ts = ts / 1000
return datetime.fromtimestamp(ts, tz=timezone.utc)
根治这个问题的两条原则
原则1:API 边界用 ISO 8601 字符串,不传裸时间戳
ISO 8601 格式是 2026-04-11T02:13:20Z,带时区、精度明确、人类可读,不存在秒/毫秒歧义。如果你能控制接口设计,优先用这种格式传时间。
// 前端发
const payload = {
created_at: new Date().toISOString() // "2026-04-11T02:13:20.123Z"
};
// 后端收
from datetime import datetime
dt = datetime.fromisoformat("2026-04-11T02:13:20.123Z".replace('Z', '+00:00'))
原则2:如果必须传时间戳,在接口文档里明确写单位
在接口文档里写清楚 created_at: Unix timestamp (seconds) 还是 (milliseconds),并且在入口处做校验:
def validate_timestamp(ts: int, unit: str = 'seconds'):
if unit == 'seconds' and ts > 1e12:
raise ValueError(f"期望秒级时间戳,收到了疑似毫秒的值: {ts}")
if unit == 'milliseconds' and ts < 1e10:
raise ValueError(f"期望毫秒级时间戳,收到了疑似秒的值: {ts}")
遇到不确定的时间戳,粘到 时间戳在线转换工具 里,自动识别秒还是毫秒,直接显示对应的日期,比自己心算省事。



加载中...