mobile wallpaper 1mobile wallpaper 2mobile wallpaper 3mobile wallpaper 4
959 字
3 分钟
刚发布的日记,Vercel部署后却显示8小时前?
2026-05-01

刚发布的日记,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));
// ...
}

问题拆解#

  1. now.getTime() 返回 UTC 毫秒数(从 1970-01-01 00:00:00 UTC 开始),与本地时区无关。
  2. now.getTimezoneOffset() 返回当前环境本地时区与 UTC 的差值(分钟),符号:本地比 UTC 为负,为正。
    • 中国(UTC+8):-480
    • Vercel 服务器(UTC+0):0
  3. 原代码先加 getTimezoneOffset()(画蛇添足),再加 timeGap * 3600000(目标偏移)。

为什么本地正常,Vercel 出错?#

环境getTimezoneOffset()utc = now.getTime() + offsetlocalNow = utc + 8hdate.getTime()差值结果
本地(UTC+8)-480分钟(-8h)UTC时间戳 -8hUTC时间戳UTC时间戳正确(-8h+8h=0)
Vercel(UTC+0)0UTC时间戳UTC时间戳 +8hUTC时间戳多8小时

本质原因: 原代码意图可能是将“当前时间”和“日记时间”都转换到目标时区再相减,但实际只转换了当前时间,且引入了依赖服务器时区的 getTimezoneOffset。本地环境中 getTimezoneOffset = -480 意外抵消了后续的 +8h,造成“正常”假象;Vercel 中 getTimezoneOffset = 0,导致当前时间多加了 8 小时,而日记时间保持 UTC,所以差值多出 8 小时。

本地模拟 Vercel 环境验证#

为了在不部署的情况下复现 bug,可以在本地开发时强制设置 Node.js 时区为 UTC。

方法一:命令行(临时)#

  • PowerShell$env:TZ="UTC"; pnpm run dev
  • CMDset TZ=UTC && pnpm run dev
  • macOS/LinuxTZ=UTC pnpm run dev

方法二:IntelliJ IDEA 配置(推荐)#

在运行配置中添加环境变量 TZ=UTC,保存后启动即可。

img

验证结果#

  • 默认本地时区(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 中的所有 getTimezoneOffsettimeGap 计算,直接使用 UTC 毫秒差。

现在,无论是在本地、Vercel 还是任何云平台,日记的相对时间都精准显示。

经验总结#

  • getTimezoneOffset() 应该用于获取用户本地时区信息,绝不应参与时间差的数学运算。

  • 在本地模拟生产环境时区(如 Vercel 的 UTC)可以快速复现 bug,推荐在 IDEA 等 IDE 中直接配置环境变量。

相关资源#

分享

如果这篇文章对你有帮助,欢迎分享给更多人!

刚发布的日记,Vercel部署后却显示8小时前?
https://cmzh.yongs.xyz/posts/时区问题导致的日记时间差计算错误/
作者
沉眠之海°
发布于
2026-05-01
许可协议
CC BY-NC-SA 4.0

部分信息可能已经过时

目录