如何按照用户的本地习惯格式化日期

使用 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);

格式化后的字符串和其他字符串一样,可以插入到文本内容、属性或任何需要向用户展示信息的场景中。