先看位数再看量级
时间戳单位认错的代价是时间整体偏移上千年,而这类错误往往不报错。判断顺序建议是两步:先数位数——同一时刻的近似值是秒 10 位、毫秒 13 位、微秒 16 位、纳秒 19 位;再用合理区间交叉验证,比如秒级时间戳应当落在 2001 年至 2033 年这一常见区间内。两步都对不上,就要怀疑这是别的编号而不是时间。
基本信息
| 秒(10 位) | 覆盖 2001-09-09 至 2033-05-18,最常见的接口单位 |
|---|---|
| 毫秒(13 位) | 覆盖 2001-09 至 2286,浏览器与不少移动端的默认单位 |
| 微秒(16 位) | Python 与部分数据库中常见,覆盖范围远至公元 5 万年后 |
| 纳秒(19 位) | 内核与高性能计时常见,超出双精度整数安全范围 |
| 交叉验证 | 把候选值代入转换函数,看结果年份是否合理 |
典型使用场景
- 对接第三方接口,文档没写单位时先取样反推
- 读数据库字段:老库常用毫秒,新库可能改成微秒
- 排查时间显示成 1970 年:多半是把秒当毫秒传了
- 排查时间显示成几万年以后:把毫秒当秒传了
- 做数据清洗,在同一列里混用多种单位的脏数据
单位识别与换算
单位识别与换算
import datetime as dt
def to_datetime(v):
s = str(int(v))
n = len(s)
if n <= 10:
return dt.datetime.fromtimestamp(int(v), dt.timezone.utc) # 秒
if n <= 13:
return dt.datetime.fromtimestamp(int(v) / 1000, dt.timezone.utc) # 毫秒
if n <= 16:
return dt.datetime.fromtimestamp(int(v) / 1e6, dt.timezone.utc) # 微秒
raise ValueError('位长超出常见范围,先确认是否为时间戳')
# 合理性校验:年份落在 2001 到 2033 之间才认秒或毫秒
for v in (1700000000, 1700000000000, 1700000000000000):
print(v, to_datetime(v).isoformat())位数与解释对照
| 位数 | 常见单位 | 误判后果 |
|---|---|---|
| 10 | 秒 | 当毫秒会得到 1970 年附近的日期 |
| 13 | 毫秒 | 当秒会得到公元 5 万年之后的日期 |
| 16 | 微秒 | 当毫秒会得到 1970 年代 |
| 17 到 19 | 纳秒或高精度计数 | 浮点换算必然丢精度,需按字符串或整数处理 |
| 7 到 9 | 很可能是业务编号 | 不要盲目当时间戳换算 |
JavaScript 的 Date 以毫秒为单位,把秒级时间戳直接传进去会得到 1970 年的日期;而 GNU date 的 @ 参数以秒为单位,把毫秒传进去会得到几万年后的时间。两端都要按单位换算,而不是靠试。
数据库字段类型也会造成单位错位:32 位整型装不下毫秒级时间戳(最大值只相当于 1970 年 1 月 25 日的毫秒值),毫秒以上精度应使用 64 位整型或带小数秒精度的时间类型,具体精度上限以官方文档为准。