功能说明
该示例把绝对时间转换成 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" }));