当你在数据库里看到 1711699200 这串数字,或是在 API 响应中见到 "created_at": 1711699200,你看到的就是 Unix timestamp(又称 Unix 时间戳)。它是计算机世界中最通用的时间表示法,几乎所有编程语言、操作系统、数据库都支持它。本文将从头说清楚 Unix timestamp 是什么、为什么要用它,以及你在开发中会遇到的各种场景。
1. Unix Timestamp 的定义
Unix timestamp 是一个整数,代表从 Unix Epoch(纪元)——即 1970 年 1 月 1 日 00:00:00 UTC——到某个特定时刻之间所经过的秒数。
例如:
| 人类可读时间(UTC) | Unix Timestamp |
|---|---|
| 1970-01-01 00:00:00 | 0 |
| 2000-01-01 00:00:00 | 946684800 |
| 2024-03-29 00:00:00 | 1711670400 |
| 2038-01-19 03:14:07 | 2147483647(32 位上限) |
需要注意的是:Unix timestamp 以 UTC(协调世界时) 为基准,本身不含时区信息。同一个 timestamp 在北京(UTC+8)、伦敦(UTC+0)、纽约(UTC-5)代表的是同一个绝对时刻,只是显示出来的本地时间不同。
2. 为什么从 1970 年开始?
这个"0 点"并没有什么天文或历法上的特别意义,纯粹是历史因素。Unix 操作系统大约在 1969 到 1970 年间开发,设计者需要选一个"时间原点",就选了 1970 年 1 月 1 日 00:00:00 UTC。此后所有与 Unix 兼容的系统都沿用了这个惯例,最终成为事实标准(de facto standard)。
你也许见过其他时间原点:Windows NT Epoch 是 1601 年 1 月 1 日,NTP 时间是 1900 年 1 月 1 日,Apple Core Data 是 2001 年 1 月 1 日。彼此之间换算时需要特别注意。
3. Unix Timestamp 的核心优势
Unix timestamp 之所以在工程领域广泛使用,原因在于几个无可取代的特性:
- 时区无关:timestamp 是绝对秒数,不依附任何时区。跨国系统只要传递 timestamp,接收端再根据本地时区转换显示,不会产生歧义。
- 纯整数、易比较:要判断事件 A 是否在事件 B 之前,只需比较两个整数大小,不需要解析日期字符串,更不需要考虑闰年、月份天数等历法规则。
- 易于存储与传输:一个 64 位整数,几乎没有空间开销。数据库字段、JSON 字段、HTTP Header 都能轻松放进去。
- 计算简单:"这篇文章是 3 天前发布的吗?"只需拿现在的 timestamp 减去文章的 timestamp,再除以 86400(一天的秒数)即可判断。
4. 毫秒级 Timestamp
随着应用对精度要求提高,许多现代系统采用毫秒(millisecond)timestamp——即 Unix Epoch 以来的毫秒数,是秒数 × 1000。例如 JavaScript 的 Date.now() 返回的就是毫秒 timestamp。
这也是常见的混淆来源。1711670400(10 位数)是秒,1711670400000(13 位数)是毫秒;把毫秒 timestamp 当秒数处理,会得到公元 5 万年左右的输出。判断方式:10 位数 = 秒,13 位数 = 毫秒(在 2001 至 2286 年间适用)。
5. 各语言获取 Unix Timestamp 的方式
# PHP
time() // 秒
round(microtime(true) * 1000) // 毫秒
# Python
import time
int(time.time()) // 秒
int(time.time() * 1000) // 毫秒
# JavaScript
Math.floor(Date.now() / 1000) // 秒
Date.now() // 毫秒
# Java
System.currentTimeMillis() / 1000 // 秒
System.currentTimeMillis() // 毫秒
# Go
time.Now().Unix() // 秒
time.Now().UnixMilli() // 毫秒
# Bash / Shell
date +%s // 秒
6. 如何将 Unix Timestamp 转换为人类可读格式?
各语言都提供了标准函数,以 PHP 为例:
// timestamp → 日期字符串
$ts = 1711670400;
echo date('Y-m-d H:i:s', $ts); // 2024-03-29 00:00:00(本机时区)
echo gmdate('Y-m-d H:i:s', $ts); // 2024-03-29 00:00:00(强制 UTC)
// 日期字符串 → timestamp
$ts = strtotime('2024-03-29 00:00:00 UTC'); // 1711670400
7. 2038 年问题(Y2K38)
当 Unix timestamp 以 32 位有符号整数(signed int32) 存储时,最大值是 2147483647,对应到 2038 年 1 月 19 日 03:14:07 UTC。超过这个时刻,32 位整数溢出后会变成负数,导致系统误判时间回到 1901 年——这就是俗称的"2038 年问题(Y2038 problem)"。
现代操作系统与大多数 64 位编程语言已将 timestamp 改用 64 位整数,可表示到约西元 2920 亿年之后。但若维护的系统仍使用旧版 32 位 C 函数库或数据库的 INT(11) 字段,就需要评估升级计划。
INT UNSIGNED 可以存到 2106 年,BIGINT 则几乎没有上限。建议新系统一律使用 BIGINT 或数据库原生的 DATETIME/TIMESTAMP 类型(通常内部已采用 64 位)。
8. 常见应用场景
- 数据库记录时间:
created_at、updated_at、deleted_at字段经常以 timestamp 存放,节省空间且查询效率高。 - API 响应:REST API 传递时间时,timestamp 比 ISO 8601 字符串更容易做数值比较与排序。
- JWT 与认证 token:JSON Web Token 的
exp(过期时间)与iat(签发时间)就是 Unix timestamp。 - 日志系统:大量日志以 timestamp 记录事件,方便依时间范围过滤。
- 前端相对时间显示:"3 分钟前"、"昨天"这类相对时间,通常由前端拿
Date.now()减去后端传来的 timestamp 计算得出。
结语
Unix timestamp 看起来只是一串数字,却是整个现代软件生态赖以运作的时间语言。理解它的原理——从 1970 年起算的秒数、无时区偏移、纯整数操作——能帮助你在开发中读懂 API 文档、调试时间相关问题,以及设计出更健壮的系统架构。下次再看到 10 位数字,你就知道那是秒,13 位数字是毫秒,而且两者背后指的都是 Unix Epoch:1970 年 1 月 1 日的零时零分零秒。