如何按照用户的本地习惯格式化时间
使用 JavaScript 根据每位用户的地区习惯显示时间
引言
世界各地的时间显示方式各不相同。美国用户通常会看到 3:30 PM 显示为 3:30 PM,而大多数欧洲用户则期望看到 15:30。如果你将时间格式写死,就默认所有用户都遵循同一种习惯。
以不熟悉的格式显示时间会让用户感到困惑。习惯 24 小时制的用户看到 3:30 PM 时,需要在脑海中转换,才能判断是上午还是下午。这种认知负担会在应用中每一个时间点上不断累积。
JavaScript 提供了 Intl.DateTimeFormat API,可自动处理时间格式化。本教程将解释为何各地时间格式不同、该 API 的工作原理,以及如何为任意地区正确格式化时间。
为什么时间格式因地区而异
不同地区形成了不同的时间显示习惯。这些习惯反映了历史传统、教育体系和文化偏好。没有一种格式是全球通用的。
在美国、加拿大、澳大利亚和菲律宾,时间采用带有 AM 和 PM 标记的 12 小时制。下午 3:30 显示为 3:30 PM。
在大多数欧洲国家、拉丁美洲和亚洲,时间采用不带 AM 或 PM 标记的 24 小时制。同一时间显示为 15:30。
小时与分钟之间的分隔符也有差异。英语国家使用冒号,而有些地区则用句点或其他标点符号。
AM 和 PM 的显示方式也不同。英语使用 AM 和 PM,西班牙语使用 a.m. 和 p.m.,有些地区甚至会把这些标记放在时间前面而不是后面。
在显示时间时,需要符合用户对小时制和具体格式规范的预期。
使用 Intl.DateTimeFormat 格式化时间
Intl.DateTimeFormat 构造函数会创建一个应用特定区域格式规范的格式化器。要格式化时间,请将区域标识符作为第一个参数传入,并在第二个参数中指定与时间相关的选项。
const formatter = new Intl.DateTimeFormat('en-US', {
hour: 'numeric',
minute: 'numeric'
});
const date = new Date('2025-03-15T15:30:00');
console.log(formatter.format(date));
// Output: "3:30 PM"
这会创建一个用于美式英语的格式化器,显示小时和分钟。hour 和 minute 选项指示格式化器包含这些时间组件。format() 方法会将 Date 对象转换为带有适当格式的字符串。
Date 构造函数接受类似 2025-03-15T15:30:00 的 ISO 8601 日期时间字符串。这会创建一个表示 2025 年 3 月 15 日下午 3:30 的 Date 对象。格式化器随后会将其转换为特定区域的时间字符串。
为不同区域格式化相同时间
通过更改传递给构造函数的区域标识符,可以为不同区域格式化相同的时间。
const date = new Date('2025-03-15T15:30:00');
const usFormatter = new Intl.DateTimeFormat('en-US', {
hour: 'numeric',
minute: 'numeric'
});
console.log(usFormatter.format(date));
// Output: "3:30 PM"
const gbFormatter = new Intl.DateTimeFormat('en-GB', {
hour: 'numeric',
minute: 'numeric'
});
console.log(gbFormatter.format(date));
// Output: "15:30"
const deFormatter = new Intl.DateTimeFormat('de-DE', {
hour: 'numeric',
minute: 'numeric'
});
console.log(deFormatter.format(date));
// Output: "15:30"
const frFormatter = new Intl.DateTimeFormat('fr-FR', {
hour: 'numeric',
minute: 'numeric'
});
console.log(frFormatter.format(date));
// Output: "15:30"
每个格式化器都会应用不同的格式规范。美国格式化器使用带有 AM/PM 的 12 小时制。英国、德国和法国格式化器均使用不带 AM/PM 的 24 小时制。
无需了解每个区域使用哪种时间格式。API 会根据区域标识符自动处理这些细节。
在时间显示中包含秒
可以添加 second 选项,以在显示小时和分钟的同时显示秒数。
const formatter = new Intl.DateTimeFormat('en-US', {
hour: 'numeric',
minute: 'numeric',
second: 'numeric'
});
const date = new Date('2025-03-15T15:30:45');
console.log(formatter.format(date));
// Output: "3:30:45 PM"
second 选项的用法与 hour 和 minute 相同。将其设置为 'numeric' 即可在输出中包含秒数。
使用 2-digit 控制数字补零
hour、minute 和 second 选项可接受两个值:'numeric' 和 '2-digit'。'numeric' 值会显示不带补零的数字,而 '2-digit' 始终以两位数字显示,前面补零。
const date = new Date('2025-03-15T09:05:03');
const numericFormatter = new Intl.DateTimeFormat('en-US', {
hour: 'numeric',
minute: 'numeric',
second: 'numeric'
});
console.log(numericFormatter.format(date));
// Output: "9:05:03 AM"
const twoDigitFormatter = new Intl.DateTimeFormat('en-US', {
hour: '2-digit',
minute: '2-digit',
second: '2-digit'
});
console.log(twoDigitFormatter.format(date));
// Output: "09:05:03 AM"
数字格式化器会将 9:05:03 AM 的小时部分显示为单个数字。两位数格式化器会将 09:05:03 AM 的小时部分前面补零。分钟和秒始终显示为两位数,因为这些值通常无论设置如何都会补零。
强制 12 小时或 24 小时制
默认情况下,API 会使用当前区域偏好的时间格式。你可以通过 hour12 选项进行覆盖。
const date = new Date('2025-03-15T15:30:00');
const hour12Formatter = new Intl.DateTimeFormat('en-GB', {
hour: 'numeric',
minute: 'numeric',
hour12: true
});
console.log(hour12Formatter.format(date));
// Output: "3:30 pm"
const hour24Formatter = new Intl.DateTimeFormat('en-US', {
hour: 'numeric',
minute: 'numeric',
hour12: false
});
console.log(hour24Formatter.format(date));
// Output: "15:30"
设置 hour12: true 可强制使用 12 小时制,即使该区域通常采用 24 小时制。设置 hour12: false 可强制使用 24 小时制,即使该区域通常采用 12 小时制。
区域设置仍然决定其他格式细节,如标点和间距。英国格式化器配合 hour12: true 时,会以小写 pm 显示 3:30 pm,而美国格式化器则会以大写 PM 显示 3:30 PM。
为用户的区域设置格式化时间
无需硬编码特定区域设置,你可以使用浏览器中用户的首选语言。navigator.language 属性会返回用户的首选语言。
const userLocale = navigator.language;
const formatter = new Intl.DateTimeFormat(userLocale, {
hour: 'numeric',
minute: 'numeric'
});
const date = new Date('2025-03-15T15:30:00');
console.log(formatter.format(date));
// Output varies by user's locale
// For en-US: "3:30 PM"
// For en-GB: "15:30"
// For de-DE: "15:30"
// For fr-FR: "15:30"
这种方式可以根据每位用户的期望显示时间,无需他们手动选择区域设置。浏览器会提供语言偏好,Intl API 会应用相应的格式化规范。
您还可以传递整个首选语言数组,以启用回退机制。
const formatter = new Intl.DateTimeFormat(navigator.languages, {
hour: 'numeric',
minute: 'numeric'
});
const date = new Date('2025-03-15T15:30:00');
console.log(formatter.format(date));
API 会使用该数组中第一个支持的语言环境。当用户的首选语言不可用时,这样可以更好地实现回退处理。
创建要格式化的时间
您可以通过多种方式创建包含时间信息的 Date 对象。最可靠的方法是使用 ISO 8601 日期时间字符串。
const time1 = new Date('2025-03-15T09:00:00');
const time2 = new Date('2025-03-15T15:30:00');
const time3 = new Date('2025-03-15T23:45:30');
const formatter = new Intl.DateTimeFormat('en-US', {
hour: 'numeric',
minute: 'numeric'
});
console.log(formatter.format(time1));
// Output: "9:00 AM"
console.log(formatter.format(time2));
// Output: "3:30 PM"
console.log(formatter.format(time3));
// Output: "11:45 PM"
ISO 8601 日期时间字符串采用 YYYY-MM-DDTHH:MM:SS 格式。T 用于分隔日期和时间。该格式没有歧义,并且在所有语言环境和时区下都能保持一致。
从时间戳格式化时间
您还可以通过 Unix 时间戳创建 Date 对象。Unix 时间戳表示自 1970 年 1 月 1 日 UTC 起的毫秒数。
const timestamp = 1710515400000; // March 15, 2025 at 3:30 PM
const date = new Date(timestamp);
const formatter = new Intl.DateTimeFormat('en-US', {
hour: 'numeric',
minute: 'numeric'
});
console.log(formatter.format(date));
// Output: "3:30 PM"
当您从 API、数据库或其他以数字表示时间的系统接收时间戳时,这种方法非常适用。
您也可以将时间戳直接传递给 format() 方法,无需先创建 Date 对象。
const formatter = new Intl.DateTimeFormat('en-US', {
hour: 'numeric',
minute: 'numeric'
});
const timestamp = 1710515400000;
console.log(formatter.format(timestamp));
// Output: "3:30 PM"
API 同时支持 Date 对象和时间戳。请根据您的代码需求选择合适的方法。
格式化当前时间
要格式化当前时间,可以创建一个不带参数的 Date 对象。这样会生成一个表示当前时刻的 Date 对象。
const formatter = new Intl.DateTimeFormat('en-US', {
hour: 'numeric',
minute: 'numeric',
second: 'numeric'
});
const now = new Date();
console.log(formatter.format(now));
// Output: "3:45:12 PM" (or current time when run)
您也可以直接传递 Date.now(),它会以数字形式返回当前时间戳。
const formatter = new Intl.DateTimeFormat('en-US', {
hour: 'numeric',
minute: 'numeric',
second: 'numeric'
});
console.log(formatter.format(Date.now()));
// Output: "3:45:12 PM" (or current time when run)
这两种方式得到的结果完全相同。
复用格式化器以提升性能
创建新的 Intl.DateTimeFormat 实例时需要加载语言环境数据并处理选项。如果需要用相同的语言环境和设置多次格式化时间,建议只创建一次格式化器并重复使用。
const formatter = new Intl.DateTimeFormat('en-US', {
hour: 'numeric',
minute: 'numeric'
});
const times = [
new Date('2025-03-15T09:00:00'),
new Date('2025-03-15T12:30:00'),
new Date('2025-03-15T18:45:00')
];
times.forEach(time => {
console.log(formatter.format(time));
});
// Output:
// "9:00 AM"
// "12:30 PM"
// "6:45 PM"
这种方法比每次都创建一个新的格式化器更高效。当需要格式化包含数百或数千个时间值的数组时,性能差异会非常明显。
在模板中格式化时间
你可以在任何向用户展示时间的地方使用 Intl.DateTimeFormat。这包括将格式化后的时间插入到 HTML 模板、在表格中显示时间,或在用户界面中展示时间戳。
const formatter = new Intl.DateTimeFormat(navigator.language, {
hour: 'numeric',
minute: 'numeric'
});
const eventStart = new Date('2025-03-15T14:00:00');
const eventEnd = new Date('2025-03-15T16:30:00');
document.getElementById('start-time').textContent = formatter.format(eventStart);
document.getElementById('end-time').textContent = formatter.format(eventEnd);
格式化后的字符串与其他字符串值一样,可以插入到文本内容、属性或任何向用户展示信息的场景中。