刚发布的日记,Vercel部署后却显示8小时前
现象
我的 Astro 博客( Mizuki 主题)有一个日记页面。在本地开发(中国 UTC+8)预览时,日记的相对时间(“刚刚”、“5分钟前”)显示正确。
但部署到 Vercel 后,刚发布的日记却显示 “8小时前”。
数据格式确认
Mizuki 官方文档明确要求:日记的 date 字段必须使用 ISO 8601 格式,即 YYYY-MM-DDTHH:mm:ssZ(带 T 和末尾 Z 的 UTC 时间)。例如:
{ ... date: "2025-01-15T10:30:00Z", ... }数据流简图
日记数据 (data/diary.ts) // 存储 UTC 时间字符串 ↓ getDiaryList()页面组件 (pages/diary.astro) // 遍历 moments ↓ 传递 moment 对象到 MomentCard卡片组件 (MomentCard.astro) // 调用 formatRelativeTime ↓工具函数 (utils/timeFormat.ts) // ⚠️ 问题代码所在formatRelativeTime 在服务端被调用,它依赖服务器当前时间来计算相对时间。本地和 Vercel 服务器时区不一致,导致计算错误。
根因分析
相对时间计算公式:差值 = 当前时间 - 日记发布时间(单位转换为分钟/小时/天)
原 formatRelativeTime(简化版):
export function formatRelativeTime(dateString: string, ...) { let timeGap = 8; // 目标时区偏移(小时) const now = new Date(); const utc = now.getTime() + now.getTimezoneOffset() * 60 * 1000; const localNow = utc + timeGap * 60 * 60 * 1000; const date = new Date(dateString); const diffInMinutes = Math.floor((localNow - date.getTime()) / (1000 * 60)); // ...}问题拆解
now.getTime()返回 UTC 毫秒数(从 1970-01-01 00:00:00 UTC 开始),与本地时区无关。now.getTimezoneOffset()返回当前环境本地时区与 UTC 的差值(分钟),符号:本地比 UTC 早为负,晚为正。- 中国(UTC+8):-480
- Vercel 服务器(UTC+0):0
- 原代码先加
getTimezoneOffset()(画蛇添足),再加timeGap * 3600000(目标偏移)。
为什么本地正常,Vercel 出错?
| 环境 | getTimezoneOffset() | utc = now.getTime() + offset | localNow = utc + 8h | date.getTime() | 差值结果 |
|---|---|---|---|---|---|
| 本地(UTC+8) | -480分钟(-8h) | UTC时间戳 -8h | UTC时间戳 | UTC时间戳 | 正确(-8h+8h=0) |
| Vercel(UTC+0) | 0 | UTC时间戳 | UTC时间戳 +8h | UTC时间戳 | 多8小时 |
本质原因:
原代码意图可能是将“当前时间”和“日记时间”都转换到目标时区再相减,但实际只转换了当前时间,且引入了依赖服务器时区的 getTimezoneOffset。本地环境中 getTimezoneOffset = -480 意外抵消了后续的 +8h,造成“正常”假象;Vercel 中 getTimezoneOffset = 0,导致当前时间多加了 8 小时,而日记时间保持 UTC,所以差值多出 8 小时。
本地模拟 Vercel 环境验证
为了在不部署的情况下复现 bug,可以在本地开发时强制设置 Node.js 时区为 UTC。
方法一:命令行(临时)
- PowerShell:
$env:TZ="UTC"; pnpm run dev - CMD:
set TZ=UTC && pnpm run dev - macOS/Linux:
TZ=UTC pnpm run dev
方法二:IntelliJ IDEA 配置(推荐)
在运行配置中添加环境变量 TZ=UTC,保存后启动即可。

验证结果
- 默认本地时区(UTC+8):日记相对时间 ✅ 正确(例如“1分钟前”)
- 设置
TZ=UTC后:日记相对时间 ❌ 多出8小时(例如“8小时前”)
默认时区正常显示

UTC时区错误显示(8小时前)

正确解法
核心原则:计算时间差只需要毫秒数相减,与任何时区偏移无关。 因为时间流逝是绝对的:UTC 差 5 分钟,北京也差 5 分钟。
修复后的代码(src/utils/timeFormat.ts):
export function formatRelativeTime( dateString: string, minutesAgo: string, hoursAgo: string, daysAgo: string,): string { const now = new Date(); const diary = new Date(dateString); const diffMs = now.getTime() - diary.getTime(); const diffMinutes = Math.floor(diffMs / 60000);
if (diffMinutes < 1) return "刚刚"; if (diffMinutes < 60) return `${diffMinutes}${minutesAgo}`; if (diffMinutes < 1440) return `${Math.floor(diffMinutes / 60)}${hoursAgo}`; return `${Math.floor(diffMinutes / 1440)}${daysAgo}`;}验证修复
应用上述修改后,再次运行 TZ=UTC 模拟环境,日记相对时间显示正确(例如“1分钟前”)。

最终修复
删除原 formatRelativeTime 中的所有 getTimezoneOffset 和 timeGap 计算,直接使用 UTC 毫秒差。
现在,无论是在本地、Vercel 还是任何云平台,日记的相对时间都精准显示。
经验总结
-
getTimezoneOffset()应该用于获取用户本地时区信息,绝不应参与时间差的数学运算。 -
在本地模拟生产环境时区(如 Vercel 的 UTC)可以快速复现 bug,推荐在 IDEA 等 IDE 中直接配置环境变量。
相关资源
如果这篇文章对你有帮助,欢迎分享给更多人!
部分信息可能已经过时





