推荐使用 Intl.RelativeTimeFormat API 实现相对时间格式,它自动适配用户语言和时区,避免手写逻辑的时区、夏令时、跨月等坑;需传入 Date 实例或毫秒数,指定 locale 和 numeric: 'auto',单位支持秒至年,特殊场景如“今天”需自行兜底。

怎么用 JavaScript 把时间戳转成“2小时前”“昨天”这种相对格式
直接用原生 JS 实现相对时间(如 2小时前、昨天、3天前)并不难,但容易忽略时区、本地化和边界情况。浏览器的 Intl.RelativeTimeFormat 是最稳妥的选择,它能自动适配用户语言和时区,比手写判断逻辑更可靠。
常见错误是直接用 Date.now() - timestamp 算毫秒差再除以 3600000 算小时——这会忽略夏令时切换、闰秒、甚至跨月/跨年时的天数不均等(比如 2 月只有 28 天)。更糟的是,硬编码中文文案会让多语言站点失效。
-
Intl.RelativeTimeFormat支持locale参数,比如'zh-CN'或navigator.language - 必须传入
numeric: 'auto',否则返回"in 2 hours"而不是"2小时前" - 单位只能是
'second'、'minute'、'hour'、'day'、'week'、'month'、'year',没有'today'这种特例,需自己兜底 - 输入时间必须是
Date实例或毫秒数,不能是字符串(new Date('2024-05-20')可能因时区解析出错,建议用new Date(timestamp))
为什么 new Date().toLocaleString() 不适合做相对时间
toLocaleString() 只能输出绝对时间(如 "2024/5/20 上午10:30"),没法表达“刚刚”“几分钟前”这种动态语义。它本质是格式化器,不是相对时间计算器。强行用它 + 手动计算差值,会重复踩上面说的时区和日历逻辑坑。
典型翻车场景:
立即学习“前端免费学习笔记(深入)”;
把任意文章或文档内容转换成精美的全屏翻页 HTML 演示文件(幻灯片风格)。当用户发来一篇文章、文档链接、Markdown 内容、或粘贴的文本,并且希望生成演示用的 HTML 时触发。支持任意内容来源:iWiki 链接、KM 链接、本地文件路径、直接粘贴的文本。生成的 HTML 文件保存到本地工作区,并通过企微直接发送给用户。触发词示例:帮我把这篇文章做成PPT、转成演示文档、生成HTML幻灯片、做个演示、转成可以演示的格式、article to deck、make a slide。
- 用户在东京发帖(+9),你在旧金山(-7)打开页面,用
new Date().getTime() - postTime算出的“2小时”其实是 18 小时偏差 - 跨月比较时,直接除以
24 * 60 * 60 * 1000得到的“天数”在 3 月 31 日 → 4 月 1 日这种边界会少算 1 天 - 把
"2024-05-20"传给new Date(),Safari 会当成 UTC 时间解析,Chrome 可能按本地时区,结果不一致
一个够用的相对时间函数怎么写(兼容 IE 的底线版)
如果项目还要支持 IE(已无 Intl.RelativeTimeFormat),就得降级为手动计算。核心原则:只比较 Date 实例的 getTime() 值,不碰 getDay()、getMonth() 这类易受时区影响的方法。
function fromNow(timestamp) {
const now = Date.now();
const diffMs = now - timestamp;
const diffSec = Math.floor(diffMs / 1000);
const diffMin = Math.floor(diffSec / 60);
const diffHour = Math.floor(diffMin / 60);
const diffDay = Math.floor(diffHour / 24);
<p>if (diffSec < 5) return '刚刚';
if (diffMin < 1) return <code>${diffSec}秒前</code>;
if (diffHour < 1) return <code>${diffMin}分钟前</code>;
if (diffDay < 1) return <code>${diffHour}小时前</code>;
if (diffDay < 2) return '昨天';
if (diffDay < 7) return <code>${diffDay}天前</code>;
if (diffDay < 30) return <code>${Math.floor(diffDay / 7)}周前</code>;
return <code>${Math.floor(diffDay / 30)}个月前</code>;
}
注意:这个版本没处理“明年”“上个月”这种向前推的场景(用户发帖时间晚于当前时间),也没做国际化,仅适用于“过去时间”的社区发帖场景。
服务端要不要预渲染相对时间文本
不要。相对时间本质是客户端状态,服务端生成的 "2小时前" 在用户打开页面时可能已过期(比如缓存了 5 分钟,实际已是 7 分钟前)。正确做法是服务端只传 ISO 时间字符串或时间戳(如 "2024-05-20T10:30:00Z" 或 1716201000000),前端用 Intl.RelativeTimeFormat 或上述函数实时计算。
唯一例外是静态博客这类完全无 JS 的场景,才考虑服务端模板里用类似 moment(from, 'now').fromNow() 渲染,但得接受它不会随用户浏览时间更新。
真正容易被忽略的点是:即使用了 Intl.RelativeTimeFormat,也要监听 visibilitychange 事件,在页面切回前台时重新计算——否则用户切走半小时再回来,“刚刚”还是显示“刚刚”。


















