← 全部文章

Unix 时间转日期 — 将任意 epoch 转换为可读日期

Unix 时间戳是自 1970-01-01 UTC 起的秒数。要将其转换为日期,先按 UTC 解码,再在任意时区格式化。本指南给出 JavaScript、Python、PHP、shell、SQL(Postgres、MySQL、SQLite、BigQuery)、Excel 和 Google Sheets 中的方法,涵盖秒与毫秒的陷阱、UTC 与本地、微秒精度、负时间戳以及 2038 年问题。

一行搞定 Unix 时间转日期

用一个例子说明整个任务:Unix 时间戳 1700000000 转换为 2023-11-14T22:13:20Z——即 2023 年 11 月 14 日(星期二)22:13:20 UTC。Unix 时间戳计的是自 1970-01-01 UTC 起的秒数,因此转换就是把这个计数按 UTC 解码,再在你需要的时区格式化。唯一会让转换出错的是单位:10 位是秒,13 位是毫秒,16 位是微秒。下表是本指南涵盖的每种语言和工具的、可直接复制的一行写法。

语言 / 工具函数期望单位默认 UTC?
JavaScriptnew Date(s * 1000)毫秒否 — 传入 timeZone 选项
Pythondatetime.fromtimestamp(s, tz=timezone.utc)仅当设置了 tz
JavaInstant.ofEpochSecond(s)是(Instant 为 UTC)
C# / .NETDateTimeOffset.FromUnixTimeSeconds(s)是(UTC 偏移)
Gotime.Unix(s, 0).UTC()需调用 .UTC()
PHPgmdate('c', s)是(gm = UTC)
Shell (GNU)date -u -d @s加 -u 即可
SQL (Postgres)TO_TIMESTAMP(s)是(TIMESTAMPTZ)
Excel / Sheets=A1/86400 + DATE(1970,1,1)

Unix 时间转日期是什么意思

Unix 时间戳是自 1970-01-01 UTC 起的秒数。要将其转换为日期,先按 UTC 解码,再在任意时区格式化。这就是一句话说清的全部问题——本指南其余部分只是每种语言的语法。所有现代编程语言都内置了一个函数,接受 Unix epoch 值并返回日期对象;你唯一需要留意的是该语言期望的是秒(Python、PHP、Linux date)、毫秒(JavaScript Date)还是微秒(PostgreSQL、BigQuery)。

快速转换规则

数一数你这个值的位数。10 位的数是 Unix 秒。13 位的数是 Unix 毫秒。16 位的数是 Unix 微秒。Unix epoch 始于 1970-01-01——因此如今(2026 年)10 位的值以 17 或 18 开头;毫秒以 175 或 176 开头后跟 10 位;微秒以 1758 或 1759 开头。在把值传给任何转换函数之前先判断单位,你的输出就会落在正确的日期,而不是 1970 年。

  • 10 位 → Unix 秒(范围 2001 至 2286 年)
  • 13 位 → Unix 毫秒(范围 2001 至 2286 年)
  • 16 位 → Unix 微秒(范围 2001 至 2286 年)
  • 接近 10^9(1,000,000,000)的数是 2001 年之前的秒——仍是有效的 Unix 时间
  • 负整数是 1970 年之前的日期——多数语言能处理;需验证
  • INT 列中超过 2^31 的数会触及 2038 年上限——见下文

在 JavaScript 中将 Unix 时间转换为日期

JavaScript 的 Date 构造函数期望毫秒,所以 Unix 秒值必须先乘以 1000。结果是一个 Date 对象;用 toISOString() 得到 UTC 的 ISO 8601,或用 Intl.DateTimeFormat 得到带时区、人类可读的字符串。务必传入显式的 timeZone 选项——否则 JavaScript 会使用运行时的本地时区,而它在开发笔记本和生产服务器上各不相同。

  • new Date(1700000000 * 1000).toISOString() // '2023-11-14T22:13:20.000Z'
  • new Date(1700000000 * 1000).toLocaleString('en-US', { timeZone: 'America/New_York' }) // tz-aware
  • new Date(1700000000000).toISOString() // milliseconds — no × 1000 needed
  • new Intl.DateTimeFormat('en-US', { timeZone: 'Asia/Tokyo', dateStyle: 'full', timeStyle: 'long' }).format(new Date(ms))
  • Detect unit at runtime: const toDate = ts => new Date(ts < 1e11 ? ts * 1000 : ts)

在 Python 中将 Unix 时间转换为日期

datetime.fromtimestamp(ts, tz=timezone.utc) 是 Python 中的标准转换。tz 参数是获得 UTC 结果所必需的;没有它,Python 会按系统本地时区返回时间戳——这是服务器上时区漂移 bug 的常见来源。utcfromtimestamp() 自 Python 3.12 起已弃用,因为它返回容易误用的 naive datetime。毫秒除以 1000;微秒除以 1,000,000——Python 直接接受小数秒。

  • from datetime import datetime, timezone
  • datetime.fromtimestamp(1700000000, tz=timezone.utc) # 2023-11-14 22:13:20+00:00
  • datetime.fromtimestamp(1700000000.123, tz=timezone.utc) # fractional seconds work
  • datetime.fromtimestamp(ms / 1000, tz=timezone.utc) # from milliseconds
  • datetime.fromtimestamp(us / 1_000_000, tz=timezone.utc) # from microseconds
  • Avoid: datetime.utcfromtimestamp(ts) — deprecated in 3.12; returns naive datetime

在 PHP 中将 Unix 时间转换为日期

PHP 的 gmdate(format, timestamp) 为给定的 Unix 秒返回 UTC 日期字符串;date(format, timestamp) 返回同一时刻,但用服务器的本地时区(由 date_default_timezone_set 或 php.ini 控制)。配合 DateTimeZone 的 DateTime 类提供现代、不可变的 API,并支持任意 IANA 时区名。PHP 默认以秒为单位,因此请先把毫秒值除以 1000。

  • gmdate('c', 1700000000) // '2023-11-14T22:13:20+00:00' (UTC ISO 8601)
  • date('Y-m-d H:i:s', 1700000000) // server-local — depends on date.timezone setting
  • new DateTime('@1700000000') // immutable, UTC by default with the @ prefix
  • (new DateTime('@1700000000'))->setTimezone(new DateTimeZone('Asia/Tokyo'))->format('c')
  • DateTime::createFromFormat('U.u', '1700000000.123456') // sub-second precision

在 Java 中将 Unix 时间转换为日期

Java 的 java.time API(Java 8+)用 Instant.ofEpochSecond(s) 转换 Unix 秒,返回一个 UTC 时刻——Instant 始终是 UTC,因此没有隐藏的本地时区陷阱。毫秒输入用 Instant.ofEpochMilli(ms),用 atZone(ZoneId.of(...)) 附加时区以获得挂钟表示,并用 DateTimeFormatter 格式化。遗留的 java.util.Date 构造函数像 JavaScript 一样期望毫秒——请先把秒乘以 1000L。

  • Instant.ofEpochSecond(1700000000) // 2023-11-14T22:13:20Z (UTC)
  • Instant.ofEpochMilli(1700000000000L) // from milliseconds
  • Instant.ofEpochSecond(1700000000).atZone(ZoneId.of("Asia/Tokyo")) // tz-aware
  • DateTimeFormatter.ISO_INSTANT.format(Instant.ofEpochSecond(s)) // ISO 8601
  • new java.util.Date(1700000000L * 1000) // legacy API — expects milliseconds

在 C#(.NET)中将 Unix 时间转换为日期

在 .NET 中,DateTimeOffset.FromUnixTimeSeconds(s) 是现代转换——它返回锚定到 UTC(+00:00)的 DateTimeOffset。毫秒输入用 FromUnixTimeMilliseconds(ms)。读取 .UtcDateTime 得到 UTC 的 DateTime,或用 TimeZoneInfo.ConvertTime 转换到特定时区。用往返说明符 "o" 格式化为 ISO 8601。内置的 Unix 方法(.NET 4.6 加入)会处理 epoch 计算,因此请避免旧的 DateTime.AddSeconds(从 1970 起)写法。

  • DateTimeOffset.FromUnixTimeSeconds(1700000000) // 2023-11-14 22:13:20 +00:00
  • DateTimeOffset.FromUnixTimeMilliseconds(1700000000000) // from milliseconds
  • DateTimeOffset.FromUnixTimeSeconds(s).UtcDateTime // DateTime in UTC
  • TimeZoneInfo.ConvertTime(dto, tzInfo) // tz-aware rendering
  • DateTimeOffset.FromUnixTimeSeconds(s).ToString("o") // ISO 8601 round-trip

在 Go 中将 Unix 时间转换为日期

Go 的 time.Unix(sec, nsec) 从 Unix 秒(加可选纳秒)构造一个 Time,但它按本地时区返回值——调用 .UTC() 来锚定。Go 1.17 加入了 time.UnixMilli(ms) 和 time.UnixMicro(us),因此你不再需要手动相除。用 RFC3339 常量格式化为 ISO 8601,用 time.LoadLocation 配合 .In(loc) 指定特定时区。

  • time.Unix(1700000000, 0).UTC() // 2023-11-14 22:13:20 +0000 UTC
  • time.UnixMilli(1700000000000).UTC() // from milliseconds (Go 1.17+)
  • time.UnixMicro(1700000000000000).UTC() // from microseconds (Go 1.17+)
  • time.Unix(1700000000, 0).Format(time.RFC3339) // ISO 8601 string
  • time.Unix(s, 0).In(loc) // tz-aware; loc from time.LoadLocation

在 shell(Linux / macOS)中将 Unix 时间转换为日期

在 Linux 上,GNU date 命令用 -d @ts 从 Unix 秒转换;在 macOS 上,BSD date 命令用 -r ts 完成相同任务。这些标志因历史实现而异——但 epoch 计算本身是相同的。-u 标志强制 UTC 输出,无论 TZ 环境变量如何。两种实现都通过 +%FORMAT 参数支持任意输出格式;最有用的格式字符串总结如下。

  • GNU (Linux): date -u -d @1700000000 // 'Tue Nov 14 22:13:20 UTC 2023'
  • GNU: date -u -d @1700000000 +'%Y-%m-%dT%H:%M:%SZ' // ISO 8601 UTC
  • BSD (macOS): date -u -r 1700000000 // same result, different flag
  • BSD: date -u -r 1700000000 +'%Y-%m-%dT%H:%M:%SZ'
  • Both: omit -u to use TZ environment variable; export TZ=America/New_York to override
  • Sub-second: date -u -d @1700000000.123 +'%Y-%m-%dT%H:%M:%S.%3NZ' (GNU only)

在 SQL(Postgres、MySQL、SQLite、BigQuery)中将 Unix 时间转换为日期

每种主流 SQL 方言都有内置函数,把 Unix 秒(或毫秒、微秒)转换为原生时间戳列。函数名各异,但每个函数期望的单位都有文档说明,并在同一数据库内保持一致。请使用专用函数,而不是用日期算术手动计算——内置函数能正确处理闰年、亚秒精度和时区转换。

  • PostgreSQL: TO_TIMESTAMP(1700000000) // returns TIMESTAMPTZ
  • PostgreSQL: TO_TIMESTAMP(unix_us / 1000000.0) // for microsecond input
  • MySQL: FROM_UNIXTIME(1700000000) // returns DATETIME in session timezone
  • MySQL: CONVERT_TZ(FROM_UNIXTIME(ts), @@session.time_zone, 'UTC') // force UTC
  • SQLite: datetime(1700000000, 'unixepoch') // returns TEXT 'YYYY-MM-DD HH:MM:SS'
  • SQLite: datetime(ms / 1000, 'unixepoch') // from milliseconds
  • BigQuery: TIMESTAMP_SECONDS(1700000000), TIMESTAMP_MILLIS(ms), TIMESTAMP_MICROS(us)
  • ClickHouse: toDateTime(1700000000), fromUnixTimestamp(1700000000)

在 Excel 中将 Unix 时间转换为日期

Excel 把日期存为序列号——自 1900-01-01 起的天数。把 Unix 时间戳转换为 Excel 日期只是单位换算:把 Unix 秒除以 86,400(每天的秒数)并加上 1970-01-01 这个日期,然后把单元格格式化为日期。公式 =A1/86400 + DATE(1970,1,1) 是标准答案,在所有现代 Excel 中都有效。对于毫秒,改为除以 86,400,000。输入公式后,选中单元格,打开“设置单元格格式”(Ctrl+1),在“数字”选项卡下选择日期格式。

  • Seconds: =A1/86400 + DATE(1970,1,1) // when A1 holds the Unix seconds value
  • Milliseconds: =A1/86400000 + DATE(1970,1,1)
  • Microseconds: =A1/86400000000 + DATE(1970,1,1)
  • Format cell as Date after entering the formula (Ctrl+1 → Number → Date)
  • Caveat: Excel's epoch is 1900-01-01, not 1970, hence the + DATE(1970,1,1) offset
  • Reverse direction (date to Unix): =(A1 - DATE(1970,1,1)) * 86400

在 Google Sheets 中将 Unix 时间转换为日期

Google Sheets 接受与 Excel 相同的标准公式,并另外提供专用的 EPOCHTODATE 函数。简写形式是 =EPOCHTODATE(A1, 1),其第二个参数是单位代码:1 表示秒,2 表示毫秒,3 表示微秒。输出是锚定到 UTC 的日期,Sheets 按表格的区域设置呈现。对于在不同时区的团队间共享的表格,请在列标题中注明这些日期应代表哪个时区。

  • =EPOCHTODATE(A1, 1) // seconds — shorthand
  • =EPOCHTODATE(A1, 2) // milliseconds
  • =EPOCHTODATE(A1, 3) // microseconds
  • =A1/86400 + DATE(1970,1,1) // canonical formula — works in Sheets too
  • Reverse direction: =(A1 - DATE(1970,1,1)) * 86400
  • Time zone applied is the sheet's locale; check File → Settings → Time zone

UTC 日期与本地日期——该显示哪个

Unix 时间戳编码的是一个时刻,而不是挂钟显示。处于不同时区的两位访客会把同一个 Unix 时间戳看作不同的挂钟日期。正确的行为取决于该日期代表什么:事件日志和审计记录应以 UTC 显示,以便跨区域比较;面向用户的日期(截止时间、会议时间、到期提醒)应以用户的本地时区显示。始终选择其一并记录下来;切勿在不一并存储来源时区的情况下,存储一个已转换为本地时间的字符串。

  • 服务器日志 → 以 UTC 显示,并明确标注列或图表坐标轴
  • 面向用户的截止时间 → 以用户的时区显示(通过 Intl.DateTimeFormat().resolvedOptions().timeZone 自动检测)
  • 周期性日程(“每周一上午 9 点 Eastern”)→ 与规则一并存储时区,每次触发时重新计算下一时刻
  • API 响应 → 带 Z 后缀的 ISO 8601(UTC)是安全的默认值;让客户端去转换
  • 数据库存储 → 始终 UTC;仅在展示层转换

边界情况:亚秒精度、负时间戳与 2038 年

三种边界情况会绊倒原本正确的代码。微秒时间戳(16 位)需要显式的单位处理;把它们当作秒会得到一个晚 1,000,000 倍的未来日期。负的 Unix 时间戳代表 1970 年之前的日期——多数语言能处理,但 Excel、Google Sheets 和某些 SQL 函数会在 1970 年截断或返回错误。2038 年问题是 32 位有符号整数溢出,发生在 2038-01-19 03:14:07 UTC;此后的值需要 BIGINT 存储或 64 位 time_t。

  • 微秒(16 位)— 在传给语言级的秒函数前先除以 1,000,000
  • 负秒 — 1970 年之前的日期;JavaScript Date、Python datetime、PHP DateTime 都能处理
  • Excel 中的负秒 — 会产生 #### 或错误日期;公式 =A1/86400 + DATE(1970,1,1) 仅用于正值
  • 2038 年 — INT 和有符号 32 位 time_t 在 2147483647 = 2038-01-19 03:14:07 UTC 溢出
  • 2038 年之后 — 存为 BIGINT(有符号 64 位可覆盖 epoch 两侧各 2920 亿年)
  • Excel 中早于 1900 年 — 应用无法呈现;转换为文本或使用支持公元前的上游库

将 Unix 时间转换为日期时的常见 bug

每当有人第一次把 Unix 时间转换为日期时,同一个 bug 都会出现:结果落在 1970 年。原因几乎总是 JavaScript 中漏掉了 × 1000,或 Python 中缺少 tz 参数,或 Excel 自动截断了数值列。配套的 bug 文章列出了全部 10 个——每一个都会出现在某个时刻转换 Unix 时间的代码中。浏览一遍,大多数就再也不会进入生产环境。

  • 日期显示为 1970 — 在期望毫秒处传入了秒(JS Date 构造函数)
  • 日期偏差恰好为你的本地 UTC 偏移 — 忘记指定 tz=timezone.utc(Python)或 timeZone 选项(JS)
  • Excel 显示 ##### 或 #VALUE! 错误 — 单元格未格式化为日期,或值为负
  • SQL FROM_UNIXTIME 返回错误的挂钟时间 — MySQL 应用 @@session.time_zone;请显式强制 UTC
  • 服务器与笔记本产生不同日期 — 系统时区不同;务必显式传入 tz

FAQ

如何从 Unix 时间戳获取日期?
判断该值是秒(10 位)还是毫秒(13 位),然后调用所用语言的内置转换:JavaScript 用 new Date(seconds * 1000),Python 用 datetime.fromtimestamp(seconds, tz=timezone.utc),PHP 用 gmdate('c', seconds),shell 用 date -u -d @seconds。
在 JavaScript 中如何从 Unix 时间戳获取日期?
将秒值乘以 1000(JavaScript 的 Date 构造函数期望毫秒),然后用 toISOString() 或 Intl.DateTimeFormat 格式化。例如:new Date(1700000000 * 1000).toISOString() 返回 "2023-11-14T22:13:20.000Z"。
在 Python 中如何从 Unix 时间戳获取日期?
使用 datetime.fromtimestamp(unix_seconds, tz=timezone.utc)。tz 参数是获得 UTC 结果所必需的;没有它,Python 会按系统本地时区返回时间戳。utcfromtimestamp() 自 Python 3.12 起已弃用。
如何将 Unix 时间转换为日期?
用正确的单位(秒或毫秒)和 UTC 时区,将该值传给所用语言内置的 epoch 转 datetime 函数。结果是同一时刻,格式化为可读日期。
Linux 时间和 Unix 时间一样吗?
是的。Linux、macOS 以及所有 Unix 衍生系统都使用相同的 epoch 基准——自 1970-01-01 00:00:00 UTC 起的秒数。date 命令、time() 系统调用和 clock_gettime() 都返回 Unix 时间。
为什么我的 Unix 时间戳转换成了 1970 年?
几乎总是因为你把秒传给了期望毫秒的函数(或 0 / null 被转为数字)。在 JavaScript 中 new Date(1700000000) 会落在 1970 年 1 月——构造函数期望毫秒。请先乘以 1000。
在 Excel 中如何将 Unix 时间转换为日期?
当 A1 中是 Unix 秒时,使用 =A1/86400 + DATE(1970,1,1)。在“设置单元格格式”对话框中将结果格式化为日期。对于毫秒,先除以 86400000:=A1/86400000 + DATE(1970,1,1)。
在 Google Sheets 中如何将 Unix 时间转换为日期?
与 Excel 相同的标准公式:=A1/86400 + DATE(1970,1,1)。Google Sheets 还接受更短的 =EPOCHTODATE(A1, 1),其第二个参数是单位(1 = 秒,2 = 毫秒,3 = 微秒)。
如何处理微秒级 Unix 时间戳(16 位)?
先除以 1,000,000 得到秒,再照常转换。Python 3.7+ 直接接受小数秒:datetime.fromtimestamp(unix_us / 1_000_000, tz=timezone.utc)。PostgreSQL TO_TIMESTAMP 和 BigQuery TIMESTAMP_MICROS 原生支持微秒。