为什么强烈推荐 Intl.format 格式化时间?

家好,很高兴又见面了,我是"高级前端进阶",由我带着大家一起关注前端前沿、深入前端底层技术,大家一起进步,也欢迎大家关注、点赞、收藏、转发!

1. 什么是 Intl.DurationFormat API

Intl.DurationFormat 对象用于语言敏感的持续时间的格式化,工作原理如下:

  • 给定一个语言环境,默认为浏览器语言环境,即 navigator.language
  • 给定一组表示不同单位(天、小时等)时间的值
  • 给定一个时间样式,例如:long、short、narrow、digital
  • 以所需的语言环境显示持续时间

例如,开发者可以如下定义持续时间:

let duration = {
	days: 1,
	hours: 5,
	minutes: 32
}

接着生成一个持续时间的本地化字符串:

let durationFormatter = new Intl.DurationFormat(navigator.language,{style: 'long'});
dur = durationFormatter.format(duration);

控制台打印结果为:'1 天 5 小时 32 分钟',而如果将语言环境更改为 fr 则输出'1 jour, 5 heures et 32 minutes'。在语言为 fr 的同时设置 short 格式则输出'1j, 5h et 32 min'。short 格式更短,而数字格式看起来更像数字时钟,更适合少于一天的持续时间。例如,可以在上面的输入中仅传入小时和分钟,则会输出:5:32:00。

// 选择 digital 类型
let duration = {
	hours: 5,
	minutes: 32
}
let durationFormatter = new Intl.DurationFormat('fr', { style: 'digital'});
dur = durationFormatter.format(duration);

更多示例可以参考下面的代码:

let $result = document.querySelector('#result');
const log = s => {$result.innerHTML += s + '
' }; let duration = { minutes: 360 } let locales = ['en','fr','de']; // 本地 let styles = ['long', 'short', 'narrow', 'digital']; // 输出的时间样式类型 locales.forEach(l => { styles.forEach(s => { let durationFormatter = new Intl.DurationFormat(l, { style: s}); log(`Locale: ${l}, Style: ${s}: ` + durationFormatter.format(duration)); }); log(''); });

2. 将 Intl.DurationFormat 与真实数据结合

既然必须以单位形式说明持续时间,那么该如何处理动态值?开发者不能简单的只取两个日期间的差,因为如果传递一个大数字,格式化程序会原样输出。例如:

let duration = {
	minutes: 360
}

输出结果是 360 分钟而非 6 小时。因此,接下来需要构建一个解决方案,尝试将时间分解为年、月、周、天、小时、分钟和秒。

首先添加两个日期字段:

接着添加一个 select 样式:

接着下方是一个空 div 用于显示结果,整体代码如下:

let $result = document.querySelector('#result');
let $style = document.querySelector('#style');
let $date1 = document.querySelector('#date1');
let $date2 = document.querySelector('#date2');
// 添加事件
$date1.addEventListener('input', handleDuration, false);
$date2.addEventListener('input', handleDuration, false);
$style.addEventListener('change', handleDuration, false);

接下来,定义一组表示持续时间单位的常量。

const MINUTE = 60 * 1000;
const HOUR = 60 * MINUTE;
const DAY = 24 * HOUR;
const WEEK = 7 * DAY;
// yeah, this is probably not perfect
const MONTH = 4 * WEEK;
const YEAR = MONTH * 12;

接下来就是表单字段更改运行的主逻辑:

function handleDuration() {
	let d1 = $date1.value;
	let d2 = $date2.value;

	if(!d1 || !d2) return;
	$result.innerHTML = '';
	d1 = new Date(d1);
	d2 = new Date(d2);
	let diff = Math.abs(d1.getTime() - d2.getTime());
	console.log(d1, d2, 'diff', diff);
	let durationFormatter = new Intl.DurationFormat(navigator.language,
													{style: $style.value});
	let result = {
		years:0,
		months:0,
		weeks:0,
		days:0,
		hours:0,
		minutes:0,
		seconds:0
	};
	if(diff>= YEAR) {
		result.years = Math.floor(diff/YEAR);
		diff -= result.years * YEAR;
	}
	if(diff>= MONTH) {
		result.months = Math.floor(diff/MONTH);
		diff -= result.months * MONTH;
	}
	if(diff>= WEEK) {
		result.weeks = Math.floor(diff/WEEK);
		diff -= result.weeks * WEEK;
	}
	if(diff>= DAY) {
		result.days = Math.floor(diff/DAY);
		diff -= result.days * DAY;
	}
	if(diff>= HOUR) {
		result.hours = Math.floor(diff/HOUR);
		diff -= result.hours * HOUR;
	}
	if(diff>= MINUTE) {
		result.minutes = Math.floor(diff/MINUTE);
		diff -= result.minutes * MINUTE;
	}
	if(diff> 0) result.seconds = diff / 1000;
	console.log('Result',result);
	let test1Result = durationFormatter.format(result);
	$result.innerHTML += `First Result: ${test1Result}`;
}

核心逻辑是根据持续时间(以毫秒为单位)是否大于一年、一个月等来构建一个持续时间结构,接着将结果传递给 format 函数并渲染。结果如下图所述:

3. Intl API 的扩展用法

除了上面提到的 DurationFormat 外,Intl 还支持 RelativeTimeFormat、DateTimeFormat 等与语言相关的时间格式化能力。当然,除了时间相关的能力外,还包括:数字(NumberFormat)、列表(ListFormat)等等其他格式化能力。

3.1 Intl.RelativeTimeFormat 相对时间格式化

除了上面语言相关的持续时间格式化外,Intl 还可用于诸多格式化场景,例如:相对时间的格式化。

const rtf1 = new Intl.RelativeTimeFormat("zh-CN", { style: "short"});
console.log(rtf1.format(3, "quarter"));
// 输出: "三个季度后"
console.log(rtf1.format(-1, "day"));
// 输出: "1 天前"
const rtf2 = new Intl.RelativeTimeFormat("es", { numeric: "auto"});
console.log(rtf2.format(2, "day"));
// 输出: "pasado ma~nana"

3.2 Intl.DateTimeFormat 格式化日期和时间

Intl.DateTimeFormat 对象能使日期和时间在特定的语言环境下格式化。

const date = new Date(Date.UTC(2020, 11, 20, 3, 23, 16, 738));
// UTC 时区
console.log(new Intl.DateTimeFormat("en-US").format(date));
// 美东时间: "12/20/2020"
console.log(new Intl.DateTimeFormat("zh-CN").format(date));
// 北京时间:"2020/12/20"
// 使用 style 配置指定时间样式,例如: full, long, medium, short 等
console.log(
  new Intl.DateTimeFormat("en-GB", {
    dateStyle: "full",
    timeStyle: "long",
    timeZone: "Australia/Sydney",
  }).format(date),
);
// 输出结果:"Sunday 20 December 2020 at 14:23:16 GMT+11"

参考资料

https://www.raymondcamden.com/2025/02/13/using-intldurationformat-for-localized-durations

https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Intl/DurationFormat

https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Reference/Global_Objects/Intl/ListFormat

https://lokalise.com/blog/date-time-localization/

https://lenguajejs.com/javascript/fechas/formatear-fechas-con-intl/

原文链接:,转发请注明来源!