Unix 时间戳深入解析:从 1970 到 2038 问题
Unix 时间戳是计算机系统中表示时间的标准方式。本文深入介绍时间戳的原理和 2038 年问题。
什么是 Unix 时间戳?
Unix 时间戳(Unix Timestamp)是从 1970年1月1日 00:00:00 UTC 开始所经过的秒数。
1970-01-01 00:00:00 UTC → 0
2024-01-01 00:00:00 UTC → 1704067200
为什么是 1970 年?
1970 年 1 月 1 日是 Unix 系统的"纪元"(Epoch),这是 Unix 系统诞生的年份。
时间戳的表示
秒级时间戳
Math.floor(Date.now() / 1000)
// 1704067200
毫秒级时间戳
Date.now()
// 1704067200000
微秒级时间戳
import time
int(time.time() * 1000000)
# 1704067200000000
各语言获取时间戳
JavaScript
// 毫秒级
Date.now()
// 秒级
Math.floor(Date.now() / 1000)
Java
// 秒级
System.currentTimeMillis() / 1000
// 毫秒级
System.currentTimeMillis()
Python
import time
# 秒级
int(time.time())
# 毫秒级
int(time.time() * 1000)
时间戳转换
时间戳 → 日期
// JavaScript
new Date(1704067200 * 1000)
// Mon Jan 01 2024 08:00:00 GMT+0800
// Java
Instant.ofEpochSecond(1704067200)
.atZone(ZoneId.of("Asia/Shanghai"))
日期 → 时间戳
// JavaScript
new Date('2024-01-01').getTime() / 1000
// Java
LocalDateTime.of(2024, 1, 1, 0, 0, 0)
.atZone(ZoneId.of("Asia/Shanghai"))
.toEpochSecond()
2038 年问题
问题描述
32 位有符号整数最大值:2,147,483,647
2,147,483,647 秒 = 2038-01-19 03:14:07 UTC
超过这个时间,32 位系统的时间戳会溢出,回到 1901 年。
影响范围
- 32 位操作系统
- 32 位应用程序
- 嵌入式设备
- 旧数据库
解决方案
- 升级到 64 位系统:使用 64 位时间戳
- 使用 long 类型:Java 中使用
long而不是int - 数据库使用 BIGINT:时间戳字段使用 64 位
// ❌ 32 位,会溢出
int timestamp = (int) (System.currentTimeMillis() / 1000);
// ✅ 64 位,不会溢出
long timestamp = System.currentTimeMillis() / 1000;
时区问题
UTC vs 本地时间
// UTC 时间
new Date().toISOString()
// '2024-01-01T00:00:00.000Z'
// 本地时间
new Date().toLocaleString()
// '2024/1/1 08:00:00'(UTC+8)
最佳实践
- 存储使用 UTC:数据库存储 UTC 时间戳
- 显示转换时区:前端根据用户时区显示
- 传输使用 ISO 8601:API 传输使用标准格式
时间戳的应用
1. 缓存过期
long expireTime = System.currentTimeMillis() / 1000 + 3600; // 1小时后
2. 签名验证
String sign = MD5(secret + timestamp + nonce);
3. 数据排序
SELECT * FROM logs ORDER BY timestamp DESC;
4. 日志记录
logger.info("[{}] User login", System.currentTimeMillis());
在线工具
使用 jsjson.com 时间戳工具 进行时间戳转换和计算。
总结
Unix 时间戳是计算机时间处理的基础。理解时间戳的原理和 2038 年问题,可以避免时间相关的 bug。