Unix时间戳 如何转换以及为何无处不在
一个关于处理Unix时间戳的实用参考:秒与毫秒的区别,JavaScript、Python、SQL和bash中的转换方法,应避免的时间区陷阱,以及在何时使用时间戳而非ISO 86及字符串。
Unix 时间戳是自 1970 年 1 月 1 日 00:00:00 UTC(也称为 Unix 时代)以来经过的秒数。这个日期并非出于技术原因被选择,而是当时设计 Unix 时的当前年份。该时间戳始终以 UTC 为准,这意味着像这样的时间戳 1712800000 在地球上每个地方都表示同一时刻,与时区无关。
Unix 时间戳被广泛使用:HTTP 头部、JWT 令牌、数据库记录、日志文件、消息队列和 API 响应。如果你正在构建或调试任何涉及时间的项目,你将频繁遇到它们。该 IO Tools Unix 时间戳转换器 对于快速查找非常有用,但你还需要知道如何在代码中处理它们。
秒与毫秒:导致所有人出错的 bug
生产环境中最常见的时间戳错误:混淆秒和毫秒。Unix 标准使用 秒。JavaScript 的 Date.now() 返回 毫秒。Java 的 System.currentTimeMillis().
也如此。时间戳 1712800000 对应的是 2024 年 4 月。时间戳 1712800000000 (毫秒) 对应的是 56000 年左右。如果你的日期计算结果是几十年后,或者显示 1970-01-01,这几乎总是原因所在。
经验之谈:如果时间戳有 13 位数字,那就是毫秒;10 位数字,那就是秒。不确定时,除以 1000,看看结果是否合理。
按语言转换 Unix 时间戳
以下是你会实际使用的模式:
JavaScript
// Get current timestamp (milliseconds — divide by 1000 for seconds)
const tsMs = Date.now(); // e.g. 1712800000000
const tsSec = Math.floor(Date.now() / 1000); // e.g. 1712800000
// Convert seconds timestamp to Date object
const ts = 1712800000;
const date = new Date(ts * 1000); // must multiply by 1000
console.log(date.toISOString()); // "2024-04-11T02:13:20.000Z"
// Convert Date back to Unix timestamp (seconds)
const tsBack = Math.floor(date.getTime() / 1000);
Python
from datetime import datetime, timezone
# Get current timestamp (seconds)
import time
ts = int(time.time()) # e.g. 1712800000
# Convert timestamp to datetime (UTC-aware)
dt = datetime.fromtimestamp(ts, tz=timezone.utc)
print(dt.isoformat()) # 2024-04-11T02:13:20+00:00
# Convert local-naive datetime to timestamp (risky — see timezone section)
ts_back = int(dt.timestamp())
SQL(MySQL / MariaDB)
-- Convert timestamp to datetime
SELECT FROM_UNIXTIME(1712800000);
-- Result: 2024-04-11 02:13:20 (server timezone applies here)
-- Get current Unix timestamp
SELECT UNIX_TIMESTAMP();
-- Convert datetime string to timestamp
SELECT UNIX_TIMESTAMP('2024-04-11 02:13:20');
Bash
# Get current timestamp
date +%s
# Convert timestamp to human-readable (GNU date)
date -d @1712800000
# Output: Thu Apr 11 02:13:20 UTC 2024
# macOS (BSD date)
date -r 1712800000
快速参考表
| 语言 | 获取当前时间(秒) | 时间戳 → 日期 | 日期 → 时间戳 |
|---|---|---|---|
| JavaScript | Math.floor(Date.now()/1000) | new Date(ts * 1000) | Math.floor(d.getTime()/1000) |
| Python | int(time.time()) | datetime.fromtimestamp(ts, tz=timezone.utc) | int(dt.timestamp()) |
| MySQL | UNIX_TIMESTAMP() | FROM_UNIXTIME(ts) | UNIX_TIMESTAMP(datetime_str) |
| Bash | date +%s | date -d @ts | date -d "2024-04-11" +%s |
时区陷阱
Unix 时间戳始终是 UTC。始终如此。数字 1712800000 没有时区——它只是从时代开始计算秒数。时区是显示问题,而不是存储问题。
开发者容易遇到的问题:
- Python 的
datetime.fromtimestamp(ts)没有tz=timezone.utc——使用服务器本地时区,静默地进行。使用fromtimestamp(ts, tz=timezone.utc)反而。 - MySQL 的
FROM_UNIXTIME()——转换为 MySQL 服务器的时区设置(@@session.time_zone)。如果您的服务器位于 UTC+8,结果将提前 8 小时。 - 在 MySQL 中存储
datetime而不进行 UTC 标准化 ——如果您的应用程序服务器和数据库服务器处于不同时区,每次插入或读取都可能成为等待发生的错误。
避免所有这些问题的模式是:在数据库中存储 Unix 时间戳,在应用程序层在显示时将其转换为可读格式,并使用用户的首选时区。
时间戳与 ISO 8601 字符串:应该存储什么
两者都是有效的存储格式。权衡如下:
- Unix 时间戳(整数):紧凑,适合比较和算术运算,没有时区歧义,适用于所有数据库引擎。缺点:没有工具就无法直接读取。
- ISO 8601 字符串 (例如
2024-04-11T02:13:20Z):在数据库中可读,自我文档化。缺点:范围查询较慢,时区可能被意外省略或错误。
对于大多数应用程序:将时间戳存储为整数。在 API 和日志中使用 ISO 8601 以确保可读性。如果必须存储 ISO 8601,始终包含 Z 或明确的偏移——一个没有时区的 2024-04-11 02:13:20 在分布式系统中是灾难。
2038 问题
32 位有符号整数能存储的最大值为 2147483647,对应于 2038 年 1 月 19 日 03:14:07 UTC。使用 32 位 int 存储 Unix 时间戳的系统将在那一刻溢出——回滚到 1901 年或崩溃。
需要担心吗?如果你编写新代码,不需要——使用 64 位整数。如果你维护旧的 C 代码、嵌入式固件或使用 INT(11) 的旧 MySQL 模式存储时间戳,值得进行审计。MySQL 的 TIMESTAMP 类型也受影响; DATETIME 不受影响。使用 64 位时间戳的现代系统可以安全运行到 2920 亿年。
需要快速转换时间戳而无需编写代码?IO Tools 的 Unix 时间戳转换器 支持秒和毫秒,同时显示 UTC 和本地时间,并允许你粘贴一个日期以获取时间戳。
