如何按照用户的本地习惯格式化日期
使用 JavaScript 根据每位用户的区域习惯显示日期
引言
世界各地的日期写法各不相同。美国人将 2025 年 3 月 15 日写作 3/15/2025,而欧洲人则写作 15/03/2025,日本用户则期望看到 2025/3/15。如果你将日期格式硬编码,就等于假设所有用户都遵循同一种习惯。
以不熟悉的格式显示日期会造成困扰。如果用户期望看到 15/03/2025,却看到 3/15/2025,就需要停下来思考日期到底是 3 月 15 日,还是不可能存在的第 15 个月。这种认知负担会在应用中的每个日期上不断累积。
JavaScript 提供了 Intl.DateTimeFormat API,可自动处理日期格式化。本文将解释为何各地日期格式不同、该 API 的工作原理,以及如何为任意本地化环境正确格式化日期。
为什么日期格式因地区而异
不同地区形成了不同的日期书写习惯。这些习惯反映了历史沿革、教育体系和文化偏好。没有一种格式是通用的。
在美国,日期采用月-日-年格式。2025 年 3 月 15 日写作 3/15/2025。
在包括英国、德国、法国和西班牙在内的大多数欧洲国家,日期采用日-月-年格式。同一天写作 15/03/2025。
在日本、中国和韩国,日期采用年-月-日格式。该日期写作 2025/3/15。
不同的本地化环境还会使用不同的分隔符。美国人用斜杠,德国人用句点,有些地区则用连字符或空格。
月份名称也会因语言而异。例如,三月在英文中为 "March",德文为 "März",法文为 "mars",西班牙文为 "marzo",日文为 "3月"。
在显示日期时,需要根据用户的期望,匹配日期各组成部分的顺序和具体的格式规范。
使用 Intl.DateTimeFormat 格式化日期
Intl.DateTimeFormat 构造函数会创建一个应用特定区域设置规范的日期格式化器。将区域标识符作为第一个参数传入,然后使用 format() 方法并传入 Date 对象。
const formatter = new Intl.DateTimeFormat('en-US');
const date = new Date('2025-03-15');
console.log(formatter.format(date));
// Output: "3/15/2025"
这会创建一个用于美式英语的格式化器,采用月/日/年格式。format() 方法会将 Date 对象转换为带有适当格式的字符串。
Date 构造函数接受像 2025-03-15 这样的 ISO 8601 日期字符串。这会创建一个表示 2025 年 3 月 15 日(UTC 零点)的 Date 对象。格式化器随后会将其转换为特定区域设置的字符串。
为不同区域设置格式化同一日期
只需更改传递给构造函数的区域标识符,就可以为不同区域设置格式化同一日期。
const date = new Date('2025-03-15');
const usFormatter = new Intl.DateTimeFormat('en-US');
console.log(usFormatter.format(date));
// Output: "3/15/2025"
const gbFormatter = new Intl.DateTimeFormat('en-GB');
console.log(gbFormatter.format(date));
// Output: "15/03/2025"
const deFormatter = new Intl.DateTimeFormat('de-DE');
console.log(deFormatter.format(date));
// Output: "15.3.2025"
const jpFormatter = new Intl.DateTimeFormat('ja-JP');
console.log(jpFormatter.format(date));
// Output: "2025/3/15"
每个格式化器都会应用不同的规范。美国格式化器使用月/日/年和斜杠分隔。英国格式化器使用日/月/年和斜杠分隔。德国格式化器使用日/月/年和句点分隔。日本格式化器使用年/月/日和斜杠分隔。
你无需了解每个区域设置使用的具体模式或分隔符。API 会根据区域标识符自动处理这些细节。
为用户的区域设置格式化日期
无需硬编码特定区域设置,可以使用浏览器中用户的首选语言。navigator.language 属性会返回用户的首选语言。
const userLocale = navigator.language;
const formatter = new Intl.DateTimeFormat(userLocale);
const date = new Date('2025-03-15');
console.log(formatter.format(date));
// Output varies by user's locale
// For en-US: "3/15/2025"
// For en-GB: "15/03/2025"
// For de-DE: "15.3.2025"
// For ja-JP: "2025/3/15"
这种方法可以根据每位用户的期望显示日期,无需用户手动选择语言环境。浏览器会提供语言偏好设置,Intl API 会应用相应的格式化规范。
你也可以传递整个首选语言数组,以启用回退机制。
const formatter = new Intl.DateTimeFormat(navigator.languages);
const date = new Date('2025-03-15');
console.log(formatter.format(date));
API 会使用数组中第一个支持的语言环境。当用户的首选语言不可用时,这种方式能更好地处理回退。
了解 API 格式化的内容
Intl.DateTimeFormat API 用于格式化 JavaScript 的 Date 对象。Date 对象表示某一特定时刻,包括日期、时间和时区信息。
当你格式化 Date 对象时,API 会根据语言环境的规范将其转换为字符串。默认情况下,API 只格式化日期部分,忽略时间部分。
const formatter = new Intl.DateTimeFormat('en-US');
const dateWithTime = new Date('2025-03-15T14:30:00');
console.log(formatter.format(dateWithTime));
// Output: "3/15/2025"
Date 对象包含时间信息,但默认格式化器会忽略它。后续课程将介绍如何同时格式化日期和时间,或仅格式化时间。
创建待格式化的日期
你可以通过多种方式创建 Date 对象。最可靠的方法是使用 ISO 8601 日期字符串。
const date1 = new Date('2025-03-15');
const date2 = new Date('2025-12-31');
const date3 = new Date('2025-01-01');
const formatter = new Intl.DateTimeFormat('en-US');
console.log(formatter.format(date1));
// Output: "3/15/2025"
console.log(formatter.format(date2));
// Output: "12/31/2025"
console.log(formatter.format(date3));
// Output: "1/1/2025"
ISO 8601 字符串采用 YYYY-MM-DD 格式。这种格式没有歧义,并且在所有语言环境和时区下都能一致工作。
通过时间戳格式化日期
你也可以通过 Unix 时间戳创建 Date 对象。Unix 时间戳表示自 1970 年 1 月 1 日 UTC 起的毫秒数。
const timestamp = 1710489600000; // March 15, 2025
const date = new Date(timestamp);
const formatter = new Intl.DateTimeFormat('en-US');
console.log(formatter.format(date));
// Output: "3/15/2025"
当你从 API、数据库或其他以数字表示日期的系统接收时间戳时,这种方法非常适用。
你也可以直接将时间戳传递给 format() 方法,无需先创建 Date 对象。
const formatter = new Intl.DateTimeFormat('en-US');
const timestamp = 1710489600000;
console.log(formatter.format(timestamp));
// Output: "3/15/2025"
该 API 同时支持 Date 对象和时间戳。你可以根据代码需求选择合适的方式。
格式化当前日期
要格式化当前日期,只需创建一个不带参数的 Date 对象。这样会生成一个表示当前时刻的 Date 对象。
const formatter = new Intl.DateTimeFormat('en-US');
const now = new Date();
console.log(formatter.format(now));
// Output: "10/15/2025" (or current date when run)
你也可以直接传递 Date.now(),它会返回当前时间戳(数字类型)。
const formatter = new Intl.DateTimeFormat('en-US');
console.log(formatter.format(Date.now()));
// Output: "10/15/2025" (or current date when run)
这两种方式的结果完全相同。
复用格式化器以提升性能
创建新的 Intl.DateTimeFormat 实例时会加载本地化数据并处理选项。如果你需要用相同的本地化和设置格式化多个日期,建议只创建一次格式化器并复用。
const formatter = new Intl.DateTimeFormat('en-US');
const dates = [
new Date('2025-01-01'),
new Date('2025-06-15'),
new Date('2025-12-31')
];
dates.forEach(date => {
console.log(formatter.format(date));
});
// Output:
// "1/1/2025"
// "6/15/2025"
// "12/31/2025"
这种方式比每次格式化都新建格式化器更高效。当你需要格式化成百上千个日期数组时,性能差异会非常明显。
在模板中格式化日期
你可以在任何向用户展示日期的地方使用 Intl.DateTimeFormat,包括插入到 HTML 模板、在表格中显示日期,或在界面中展示时间戳。
const formatter = new Intl.DateTimeFormat(navigator.language);
const publishedDate = new Date('2025-03-15');
const updatedDate = new Date('2025-04-20');
document.getElementById('published').textContent = formatter.format(publishedDate);
document.getElementById('updated').textContent = formatter.format(updatedDate);
格式化后的字符串和其他字符串一样,可以插入到文本内容、属性或任何需要向用户展示信息的场景中。