← 中文资源

简体中文版本

用 Intl 生成相对时间文案

把时间戳格式化为本地化的相对时间文案,并缓存 Intl.RelativeTimeFormat 实例。

阅读英文原文

功能说明

该示例把绝对时间转换成 2 hours ago、in 2 Minuten 或上个月这类本地化相对文案。它接受 Date 对象、ISO 字符串或毫秒时间戳,并按秒、分、时、天、月、年逐级选择合适的单位。适合评论时间、通知列表、审计日志等需要快速理解时间距离的界面。

关键代码

DIVISIONS 按从大到小排列,find 找到第一个小于等于绝对差值的单位,避免把 90 分钟显示成 90 minutes。差值除以单位秒数后四舍五入,正负号交给 Intl.RelativeTimeFormat 处理。formatter 使用 Map 按 locale 缓存,避免每次渲染都创建开销较大的国际化对象。numeric 设为 auto,因此昨天会显示 yesterday 而不是 1 day ago。

使用注意

月和年按 30 天与 365 天估算,只适合粗略展示,不适合精确日期计算。locale 不合法时 Intl 构造函数会抛 RangeError,应在边界处捕获或提前校验。时间戳与 now 必须为有限数字,否则函数抛出 TypeError。服务端与客户端时区不同会得到不同文案,需要固定 now 或在客户端渲染。

验证方式

以固定 now 断言两小时前显示 2 hours ago,九十秒后显示德语的 in 2 Minuten,四十五天前显示中文的上个月。测试 Date、ISO 字符串与数字三种输入得到一致结果。再断言无效日期、非法 now 以及不支持的语言分别进入错误分支。

代码示例

const DIVISIONS = [
  { unit: "year", seconds: 365 * 24 * 60 * 60 },
  { unit: "month", seconds: 30 * 24 * 60 * 60 },
  { unit: "day", seconds: 24 * 60 * 60 },
  { unit: "hour", seconds: 60 * 60 },
  { unit: "minute", seconds: 60 },
  { unit: "second", seconds: 1 },
];

const formatters = new Map();

function getFormatter(locale) {
  let formatter = formatters.get(locale);
  if (formatter === undefined) {
    formatter = new Intl.RelativeTimeFormat(locale, { numeric: "auto" });
    formatters.set(locale, formatter);
  }
  return formatter;
}

export function formatRelativeTime(target, options = {}) {
  const { now = Date.now(), locale = "en" } = options;
  const targetTime =
    target instanceof Date ? target.getTime() : typeof target === "string" ? Date.parse(target) : target;
  if (!Number.isFinite(targetTime)) throw new TypeError("target must be a valid Date, ISO string or timestamp");
  if (!Number.isFinite(now)) throw new TypeError("now must be a finite timestamp");
  const deltaSeconds = Math.round((targetTime - now) / 1000);
  const magnitude = Math.abs(deltaSeconds);
  const division =
    DIVISIONS.find((item) => magnitude >= item.seconds) ?? DIVISIONS[DIVISIONS.length - 1];
  const value = Math.round(deltaSeconds / division.seconds);
  return getFormatter(locale).format(value, division.unit);
}

const now = Date.parse("2026-10-21T12:00:00Z");
console.log(formatRelativeTime("2026-10-21T10:00:00Z", { now, locale: "en" }));
console.log(formatRelativeTime(now + 90_000, { now, locale: "de" }));
console.log(formatRelativeTime(now - 45 * 24 * 60 * 60 * 1000, { now, locale: "zh-CN" }));