前端进阶之旅前端进阶之旅
基础篇
进阶篇
高频篇
精选篇
手写篇
面经篇
AI 篇
原理篇
每日一题
小程序题库
知识卡片
  • 场景篇按分类整理的大前端场景考点
  • 历年面经按年份追踪真实考点
  • 算法题库NEW在线编码即时判题
  • 专项自测100 题快速查漏
  • 前端基础
    • HTTP从报文一路讲到 HTTPS
    • 浏览器渲染、事件循环、进程
    • 计算机基础Linux、网络、操作系统
  • 进阶专项
    • 设计模式23 种模式怎么用
    • 前端系统进阶学习大型项目工程化
    • 前端综合文章长期沉淀的实践文
  • 工程与工具
    • Node学习指南从环境搭建到服务端
    • NPM工作流script、依赖与发布
    • Docker容器化部署上手
    • Canvas图形与动画实战
  • 路线与导图
    • 思维导图知识点全景图
    • 学习路线按图索骥不跑偏
  • 动态
    • 公众号动态公众号历史文章
    • 博客动态站长的技术博客
    • 开发者导航常用工具与文档站
  • AI 助手随时提问,即时解析
  • AI 模拟面试模拟真实面试 + 报告
  • AI 知识地图串起全站知识点
  • AI 定制路线按你的简历现排
AI 热点
旧版
基础篇
进阶篇
高频篇
精选篇
手写篇
面经篇
AI 篇
原理篇
每日一题
小程序题库
知识卡片
  • 场景篇按分类整理的大前端场景考点
  • 历年面经按年份追踪真实考点
  • 算法题库NEW在线编码即时判题
  • 专项自测100 题快速查漏
  • 前端基础
    • HTTP从报文一路讲到 HTTPS
    • 浏览器渲染、事件循环、进程
    • 计算机基础Linux、网络、操作系统
  • 进阶专项
    • 设计模式23 种模式怎么用
    • 前端系统进阶学习大型项目工程化
    • 前端综合文章长期沉淀的实践文
  • 工程与工具
    • Node学习指南从环境搭建到服务端
    • NPM工作流script、依赖与发布
    • Docker容器化部署上手
    • Canvas图形与动画实战
  • 路线与导图
    • 思维导图知识点全景图
    • 学习路线按图索骥不跑偏
  • 动态
    • 公众号动态公众号历史文章
    • 博客动态站长的技术博客
    • 开发者导航常用工具与文档站
  • AI 助手随时提问,即时解析
  • AI 模拟面试模拟真实面试 + 报告
  • AI 知识地图串起全站知识点
  • AI 定制路线按你的简历现排
AI 热点
旧版
首页程序员面试题库Intl.RelativeTimeFormat 相对时间格式化
JavaScript数据处理

评论区的「5 分钟前」「昨天」,用 Intl.RelativeTimeFormat 怎么实现?

new Intl.RelativeTimeFormat('zh-CN').format(-5, 'minute') 输出「5分钟前」,正数输出「5分钟后」。加上 numeric: 'auto',-1 天会说成「昨天」而不是「1天前」。它只负责把数值和单位说成人话,时间差怎么算、选秒还是选天,都要你自己决定。

前端进阶之旅 · 一题精讲更新于 2026.09.29
JavaScript#数据处理
先看核心答案读代码示例
理解线索

先算时间差,再交给它措辞

  1. value带符号的数值,负数是过去,正数是未来,0 是当前
  2. unitsecond、minute、hour、day 这些单位,由你按差值大小选
  3. numeric默认 always 总是用数字,auto 在语言支持时换成昨天、明天这类词

它不会自动刷新。「5分钟前」的文字过了十分钟还是「5分钟前」,要自己设定时器定期重新格式化,页面不可见时可以暂停。

核心回答

先记住这个答案

Intl.RelativeTimeFormat 的 format(value, unit) 把一个数值和单位格式化成相对时间。value 的正负决定方向:format(-5, 'minute') 是「5分钟前」,format(5, 'minute') 是「5分钟后」。构造时传 numeric: 'auto',遇到 -1 天、1 天、0 天这种有专门说法的情况,会输出「昨天」「明天」「今天」。它只负责措辞,不接收两个 Date。算时间差、决定用秒还是用天、怎么取整,都是调用方的事。

  • 负数表示过去,正数表示未来
  • numeric: 'auto' 会输出昨天、明天、上周
  • 它不接收两个 Date,时间差和单位要自己算
  • 按 24 小时算「昨天」,跨午夜和夏令时会不准

numeric 的两种模式

同一组数值,默认模式和 auto 模式的输出对比一下就清楚了。

always 和 auto 对比JavaScript
const always = new Intl.RelativeTimeFormat('zh-CN') // 默认 numeric: 'always'
const auto = new Intl.RelativeTimeFormat('zh-CN', { numeric: 'auto' })

const cases = [[-5, 'minute'], [-1, 'day'], [0, 'day'], [1, 'day'], [-1, 'week']]
for (const [value, unit] of cases) {
  console.log(value, unit, always.format(value, unit), auto.format(value, unit))
}
查看输出与解释
-5 minute 5分钟前 5分钟前
-1 day 1天前 昨天
0 day 0天后 今天
1 day 1天后 明天
-1 week 1周前 上周

默认模式总是用数字,0 天会说成「0天后」,很别扭。auto 模式在语言有专门词汇时换成自然说法,其他情况和默认模式一样。评论、消息列表这类场景基本都用 auto。 单位写单数 minute 或复数 minutes 都可以,规范两种都接受。但写成 min 这种缩写会抛 RangeError。

自己写一个自动选单位的 timeAgo

实际业务拿到的是一个时间戳,要自己算出差值,再按大小选合适的单位。下面是一个最简版本,now 作为参数传入,方便测试。

按阈值选单位再格式化JavaScript
const rtf = new Intl.RelativeTimeFormat('zh-CN', { numeric: 'auto' })
const UNITS = [['year', 31536000], ['month', 2592000], ['day', 86400], ['hour', 3600], ['minute', 60]]

function timeAgo(target, now) {
  const seconds = Math.round((target - now) / 1000) // 过去为负,未来为正
  for (const [unit, size] of UNITS) {
    if (Math.abs(seconds) >= size) return rtf.format(Math.trunc(seconds / size), unit)
  }
  return rtf.format(seconds, 'second')
}

const now = new Date('2026-09-10T12:00:00Z')
console.log(timeAgo(new Date('2026-09-10T11:55:00Z'), now))
console.log(timeAgo(new Date('2026-09-09T12:00:00Z'), now))
console.log(timeAgo(new Date('2026-06-10T12:00:00Z'), now))
console.log(timeAgo(new Date('2026-09-10T12:00:30Z'), now))
查看输出与解释
5分钟前
昨天
3个月前
30秒钟后

思路是从大单位往小单位找,第一个「差值大于等于这个单位长度」的就用它,然后除以单位长度取整。target - now 让过去的时间自然得到负数,直接交给 format(),不用自己拼「前」「后」。 月按 30 天、年按 365 天算是近似值,对评论区够用了。Math.trunc 是向零取整,89 秒会显示成「1分钟前」;想要四舍五入就换成 Math.round,但要注意 59 分 40 秒会被说成「60分钟前」,最好再处理一下进位。

「昨天」按 24 小时算并不准
上面的写法里,「昨天」其实是「24 到 48 小时之前」。晚上 11 点看早上 1 点发的评论,差了 22 小时,显示「22小时前」,但按日历它是今天。真正的「昨天」是日历概念,要按用户时区比较两个时间的日期,还要考虑夏令时那天只有 23 或 25 小时。对准确性有要求的场景,比如聊天记录的日期分组,用日期差而不是毫秒差。
回答前,多想一步

容易答错的地方

把过去的时间传成正数
正数是未来。算差值时写成 now - target,过去的时间就变成了正数,页面上显示「5分钟后」。统一用 target - now,过去为负。
以为它能直接接收两个 Date
format() 只接收数值和单位,传 Date 进去不会帮你算差值。选单位、取整、阈值这些都是产品规则,每个产品不一样,所以 API 故意没有包办。
试着用自己的话回答

面试官还会怎么问?

「刚刚」这种说法怎么实现?

Intl.RelativeTimeFormat 在 auto 模式下 format(0, 'second') 会输出「现在」。产品想要「刚刚」,可以在差值小于某个阈值时直接返回自己的文案,这属于产品措辞,不走格式化器。

formatToParts() 用在什么地方?

需要把数字单独加粗或换颜色时用。英文 formatToParts(-5, 'minute') 会拆成数字 5 和文本「 minutes ago」两段,按顺序渲染就不会破坏语序。

列表里几百条评论都要显示相对时间,怎么做?

formatter 建一个全局复用,不要每条都 new。定时刷新也只用一个定时器,比如每分钟统一重新计算一次,而不是每条评论挂一个 setInterval。

从一道题,走向一组知识

把知识连起来

数据处理

中文没有空格怎么分词?emoji 的长度为什么不对?Intl.Segmenter 怎么用?

同属 Intl 文本处理 API,一个把时间差说成人话,一个按语言把句子拆成词。

数据处理

用户的语言浏览器不支持时,Intl 会怎么回退?应用要怎么兜底?

用户语言不在支持列表里时,「5分钟前」会回退成哪种语言,要靠 supportedLocalesOf 和语言协商来确认。

参考资料

  • JavaScript:数据处理参考资料

示例用于理解所注明的运行环境与边界;延伸学习可结合原文中的更多案例。

本题目录
  1. 先记住这个答案
  2. numeric 的两种模式
  3. 自己写一个自动选单位的 timeAgo
  4. 容易答错的地方
  5. 面试官还会怎么问
  6. 把知识连起来
读懂,再试着讲出来

先看核心答案,再读代码。最后展开追问,检查自己有没有遗漏边界。

试着回答追问
浏览全部面试题理解原理,也关注真实的使用场景。回到顶部 ↑