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

boyanx2个月前技术教程14

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

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/

相关文章

CSS视觉格式化模型,你真的了解么?

CSS视觉格式化模型(visual formatting model)是用来处理文档并将它显示在视觉媒体上的机制。这是CSS 2.1的一个基础概念。视觉格式化模型根据CSS盒模型为文档的每个元素生成0...

CSS布局基础——BFC

what's BFC?第一次看到这个名词,我是拒绝的,css什么时候还有这个东西?于是迫不及待的google了一下,才发现原来它无时无刻不在我们的css当中,只不过它并不是一个属性,不需要我们平常使用...

格式化字符串漏洞及利用_萌新食用

前言格式化字符串漏洞 具有 任意地址读,任意地址写。printfprintf --一个参数:情况1当参数 只有 1个字符串的话(含有%?), //? 即 i, x, s 等等第一个参数 作为 格式化字...

Python之文本解析:字符串格式化的逆操作?

引言前面的文章中,提到了关于Python中字符串中的相关操作,更多地涉及到了字符串的格式化,有些地方也称为字符串插值操作,本质上,就是把多个字符串拼接在一起,以固定的格式呈现。关于字符串的操作,其实还...

什么是Markdown,如何使用它?

Markdown是一种简单的语法,它以标题、列表、粗体等形式格式化文本,这种标记语言很流行,你肯定有一些应用程序支持它。下面是一个Markdown的简单示例,介绍了什么是Markdown,如何和在哪里...

Tailwind CSS 4.0进行了“彻底重写”

其他开发者新闻:研究发现仅 16% 的组织拥有标准化的开发者环境,Bun 更新,以及 JavaScript 的 Temporal 对象。译自 Tailwind CSS Gets a ‘Ground-U...

发表评论    

◎欢迎参与讨论,请在这里发表您的看法、交流您的观点。