很多前端项目在展示文章发布时间、评论时间或操作日志时,并不想直接输出“2024-05-20 14:30:00”这样的绝对时间戳,而是希望显示“3分钟前”“昨天下午2点”“下周星期三”等更符合人类阅读习惯的相对时间。过去的常见做法是引入moment.js、dayjs等日期库,并配合自定义的规则函数来实现相对时间格式化。然而随着浏览器内置国际化能力的增强,原生JavaScript已经提供了Intl.RelativeTimeFormat构造函数,可以非常方便地生成本地化的相对时间短语。本文的重点不在于重复讲解这个API的全部细节,而是聚焦一个实际问题:假如你的项目已经在使用jQuery,如何利用它来检测浏览器是否支持Intl.RelativeTimeFormat,并根据检测结果给出对应的显示策略。

为什么需要检测Intl.RelativeTimeFormat的支持性
Intl.RelativeTimeFormat是ECMAScript国际化API的一部分,它允许开发者将数值和单位(如天、小时、分钟)组合成符合某个区域语言习惯的短语。例如在中文环境下,new Intl.RelativeTimeFormat('zh', { numeric: 'auto' }).format(-3, 'day')会返回“3天前”,而在英文环境下则返回“3 days ago”。这个特性在现代浏览器中已经得到了广泛支持,包括Chrome 71以上、Firefox 65以上、Safari 14以上以及Edge 79以上。但仍有相当数量的用户停留在较旧的浏览器版本,尤其是部分Android内置浏览器或企业级WebView中,直接调用Intl.RelativeTimeFormat会导致脚本报错,从而中断整个页面逻辑。
因此,在功能实现之前先做一次能力检测是极其必要的。检测本身并不复杂,甚至不需要jQuery也能完成,但既然项目中已经引入了jQuery,我们完全可以利用jQuery的DOM就绪事件和工具方法把检测逻辑封装得更规整一些。检测的核心是判断全局对象上是否存在Intl.RelativeTimeFormat这个构造函数,如果存在并且可以正常实例化,就认为支持;否则回退到自定义的相对时间格式化函数。这种“渐进增强”的策略可以保证旧环境下的用户体验不降级。
使用jQuery检测Intl.RelativeTimeFormat是否存在
jQuery没有专门针对Intl.RelativeTimeFormat的检测方法,但我们可以直接借助JavaScript的typeof操作符,在jQuery的ready回调或任意合适的位置执行检测。最简单的检测代码如下:
$(function() {
if (typeof Intl !== 'undefined' && typeof Intl.RelativeTimeFormat === 'function') {
console.log('当前浏览器支持Intl.RelativeTimeFormat');
} else {
console.log('当前浏览器不支持Intl.RelativeTimeFormat,请降级处理');
}
});
这段代码使用了jQuery的文档就绪函数$(function() { ... }),确保在DOM构建完成后再执行检测。检测条件包含两部分:先判断Intl对象是否存在,再判断Intl.RelativeTimeFormat的类型是否为函数。之所以要分两步判断,是因为某些旧版浏览器可能根本没有定义Intl对象,直接访问Intl.RelativeTimeFormat会抛出ReferenceError。而typeof对未声明的变量是安全的,不会报错,因此这种写法非常稳妥。
实际项目中,我们不希望每次使用时都重复写检测逻辑,可以将其封装成一个独立的工具函数,并挂载到jQuery对象或全局作用域中。下面的示例展示了如何通过jQuery的extend方法扩展一个全局工具对象:
(function($) {
$.relativeTime = {
supported: false,
check: function() {
this.supported = typeof Intl !== 'undefined' && typeof Intl.RelativeTimeFormat === 'function';
return this.supported;
}
};
})(jQuery);
$(function() {
$.relativeTime.check();
if ($.relativeTime.supported) {
console.log('原生相对时间格式化可用');
} else {
console.log('原生相对时间格式化不可用,准备降级');
}
});
这段代码使用了立即执行函数表达式,将jQuery作为参数传入,然后通过$.relativeTime暴露一个包含supported属性和check方法的对象。这样在页面加载完成后只需要调用一次check方法,就可以在其他任何地方通过读取supported属性来得知支持状态。当然,如果你不希望扩展jQuery全局对象,也可以把检测结果存放到一个普通的JavaScript变量中,根据项目结构灵活选择即可。
基于Intl.RelativeTimeFormat实现时间友好显示
检测完成之后,如果浏览器支持Intl.RelativeTimeFormat,就可以使用它来生成相对时间短语。一个常见的需求是:给定一个过去或未来的时间点,计算出与当前时间的差值,然后将差值转换为合适的单位,再调用format方法输出。下面的示例展示了如何编写一个通用的相对时间格式化函数:
function formatRelativeTime(date, locale) {
if (typeof Intl === 'undefined' || typeof Intl.RelativeTimeFormat !== 'function') {
// 不支持时的降级逻辑,稍后介绍
return fallbackFormatRelativeTime(date);
}
const now = new Date();
const target = new Date(date);
const diffInSeconds = Math.round((target - now) / 1000);
const units = [
{ unit: 'year', seconds: 60 * 60 * 24 * 365 },
{ unit: 'month', seconds: 60 * 60 * 24 * 30 },
{ unit: 'week', seconds: 60 * 60 * 24 * 7 },
{ unit: 'day', seconds: 60 * 60 * 24 },
{ unit: 'hour', seconds: 60 * 60 },
{ unit: 'minute', seconds: 60 },
{ unit: 'second', seconds: 1 }
];
let selectedUnit = 'second';
let value = diffInSeconds;
for (const item of units) {
if (Math.abs(diffInSeconds) >= item.seconds) {
selectedUnit = item.unit;
value = Math.round(diffInSeconds / item.seconds);
break;
}
}
const rtf = new Intl.RelativeTimeFormat(locale || 'zh', {
numeric: 'auto',
style: 'long'
});
return rtf.format(value, selectedUnit);
}
这个函数接收一个日期对象或日期字符串,以及一个可选的区域标识(locale)。它首先计算目标时间与当前时间的秒数差,然后从大到小遍历单位数组,找到第一个能够覆盖该差值的单位,并计算出对应的数值。最后实例化Intl.RelativeTimeFormat并调用format方法。将numeric设置为auto可以让输出更自然,例如在中文中-1 day会输出“昨天”,-2 day输出“前天”,而不是“1天前”“2天前”。style设置为long会输出完整短语,例如“3天前”;也可以设置为short输出“3天前”的短格式,或者narrow输出更紧凑的形式。
需要注意的是,Intl.RelativeTimeFormat的format方法接受两个参数:数值和单位。数值可以为负数表示过去,正数表示未来。单位必须是Intl.RelativeTimeFormat所支持的合法单位,包括year、quarter、month、week、day、hour、minute和second。在循环中我们使用了数组来定义单位和对应的秒数阈值,这种写法便于维护和调整,例如你觉得一个月应该按31天还是30天计算,都可以灵活修改。
不支持Intl.RelativeTimeFormat时的降级方案
当检测结果显示浏览器不支持Intl.RelativeTimeFormat时,我们需要准备一套备用的相对时间格式化逻辑,以保证页面仍然能够正常显示时间信息。降级方案通常有两种思路:一种是引入外部库,比如dayjs的relativeTime插件;另一种是自己编写一个简单的规则函数,只处理中文或英文等常见语言,满足基本需求。对于不想增加额外依赖的小型项目来说,自己实现一个简易版本往往更加轻量。
下面是一个基于中文环境的降级函数示例,它根据时间差返回相应的中文短语:
function fallbackFormatRelativeTime(date) {
const now = new Date();
const target = new Date(date);
const diffSeconds = Math.round((target - now) / 1000);
const absSeconds = Math.abs(diffSeconds);
const isFuture = diffSeconds > 0;
if (absSeconds < 60) {
return isFuture ? '即将' : '刚刚';
}
const intervals = [
{ label: '年', seconds: 60 * 60 * 24 * 365 },
{ label: '个月', seconds: 60 * 60 * 24 * 30 },
{ label: '周', seconds: 60 * 60 * 24 * 7 },
{ label: '天', seconds: 60 * 60 * 24 },
{ label: '小时', seconds: 60 * 60 },
{ label: '分钟', seconds: 60 }
];
for (const interval of intervals) {
if (absSeconds >= interval.seconds) {
const value = Math.round(absSeconds / interval.seconds);
return isFuture ? value + interval.label + '后' : value + interval.label + '前';
}
}
return isFuture ? '片刻后' : '片刻前';
}
这个降级函数严格遵循“简单可靠”的原则,不依赖任何外部库,只使用原生Date对象进行计算。对于小于一分钟的时间差,统一返回“刚刚”或“即将”;对于更大的时间差,则按照年、月、周、天、小时、分钟的顺序进行匹配,最后拼接出“3天前”“2小时后”这样的短语。需要注意的是,降级函数并不会像Intl.RelativeTimeFormat那样考虑复数形式或语言区域的细微差别,因此它只适用于特定语言。如果你需要支持多语言,建议在降级分支中引入dayjs并配置对应的locale包,或者让后端返回已经格式化好的时间文本。
在实际项目中,你可以将上面的fallback函数与前面的formatRelativeTime函数结合使用。当检测到不支持Intl.RelativeTimeFormat时,自动调用降级函数。完整的调用示例如下:
$(function() {
var support = typeof Intl !== 'undefined' && typeof Intl.RelativeTimeFormat === 'function';
var publishTime = '2024-05-18T10:30:00';
if (support) {
console.log(formatRelativeTime(publishTime, 'zh'));
} else {
console.log(fallbackFormatRelativeTime(publishTime));
}
});
这段代码在文档就绪后检测支持状态,然后根据需要调用不同的格式化函数。为了保持代码整洁,你也可以将检测和格式化逻辑统一封装到一个方法中,对外只暴露一个接口。例如使用jQuery的插件模式,把相对时间格式化做成一个jQuery插件,这样调用起来会更符合jQuery的使用习惯。
实际项目中的注意事项与性能考虑
虽然Intl.RelativeTimeFormat的使用非常简单,但在真实项目里还是有几个细节值得关注。首先是关于时间差计算的精度问题。在上面的示例中,我们使用Math.round进行四舍五入,这可能会导致边界情况下的显示不够准确。例如距离现在还有59.6秒时,四舍五入后可能得到60秒,然后格式化为“1分钟后”,但实际上还不到一分钟。如果你希望输出更精确的结果,可以使用Math.floor或Math.ceil,具体取决于你想向上还是向下取整。对于大多数社交类应用来说,“刚刚”和“1分钟前”之间的切换并不会对用户造成困扰,因此不必过于纠结。
其次是关于时区的问题。Intl.RelativeTimeFormat在格式化时并不关心输入日期所在的时区,它只处理已经计算好的差值。因此,在计算时间差时一定要确保两个日期使用的是同一套时间基准。推荐使用Date对象的getTime方法获取毫秒时间戳再进行减法运算,这样无论浏览器运行在哪个时区,得到的结果都是正确的UTC时间差。
最后是性能方面的考虑。如果页面上有大量需要显示相对时间的地方(例如长列表中的每一条评论),频繁创建Intl.RelativeTimeFormat实例可能会有轻微的性能开销。虽然现代JavaScript引擎对这类对象的创建已经做了优化,但更好的做法是复用同一个实例,因为同一个locale和options下的Intl.RelativeTimeFormat对象可以被多次调用format方法,只需要改变数值和单位参数。因此,你可以在检测支持后,先在全局作用域中创建好实例,然后在格式化函数中直接引用它,而不是每次调用都new一个新的对象。
总结来说,jQuery在这套方案中的角色是提供统一的事件入口和工具扩展,真正完成相对时间显示的是浏览器内置的Intl.RelativeTimeFormat或者我们自己编写的降级函数。只要在页面初始化时做好能力检测,并根据检测结果选择合适的代码分支,就能在保证兼容性的同时,为现代浏览器用户提供原汁原味的本地化时间体验。
jQueryIntl.RelativeTimeFormat时间友好显示修改时间:2026-08-27 05:31:08