Intl.DateTimeFormat API
无需外部库即可为任何区域格式化日期和时间
介绍
世界各地的日期显示方式不同。2024 年 1 月 15 日在美国显示为 1/15/2024,在英国显示为 15/1/2024,在日本显示为 2024/1/15。时间格式也有所不同。美国人使用带有 AM 和 PM 的 12 小时制,而世界大多数地区使用 24 小时制。
为不同地区手动构建日期格式化逻辑既复杂又容易出错。您需要处理日期和月份的顺序、分隔符、时间格式、时区转换以及非公历等特殊情况。
Intl.DateTimeFormat API 解决了这个问题。它在所有现代浏览器中提供了内置的、支持区域设置的日期和时间格式化功能,无需外部库。
基本用法
格式化日期的最简单方法是创建一个 Intl.DateTimeFormat 实例并调用其 format() 方法。
const date = new Date(2024, 0, 15, 14, 30);
const formatter = new Intl.DateTimeFormat("en-US");
formatter.format(date);
// "1/15/2024"
更改区域设置以查看不同的格式约定。
const ukFormatter = new Intl.DateTimeFormat("en-GB");
ukFormatter.format(date);
// "15/01/2024"
const japanFormatter = new Intl.DateTimeFormat("ja-JP");
japanFormatter.format(date);
// "2024/1/15"
同一个日期,根据本地约定以三种不同的方式格式化。
了解区域设置
区域设置是一个标识语言和区域偏好的字符串。其格式遵循 BCP 47 标准:语言代码后可选地跟随区域代码。
常见的区域设置模式:
"en" // 英语(通用)
"en-US" // 英语(美国)
"en-GB" // 英语(英国)
"es" // 西班牙语(通用)
"es-MX" // 西班牙语(墨西哥)
"es-ES" // 西班牙语(西班牙)
"zh-CN" // 中文(中国,简体)
"zh-TW" // 中文(台湾,繁体)
如果省略区域设置参数,浏览器将使用用户的默认区域设置。
const formatter = new Intl.DateTimeFormat();
formatter.format(date);
// 输出取决于用户的浏览器区域设置
您还可以提供一个区域设置数组。浏览器将使用第一个支持的区域设置。
const formatter = new Intl.DateTimeFormat(["es-MX", "es", "en"]);
// 如果可用,使用西班牙语(墨西哥),否则回退到通用西班牙语,然后是英语
格式选项概述
Intl.DateTimeFormat 构造函数接受一个选项对象,用于控制格式化输出的内容。有两种格式化方法。
第一种方法使用样式快捷方式。这些快捷方式提供快速、常规的格式化。
const formatter = new Intl.DateTimeFormat("en-US", {
dateStyle: "full",
timeStyle: "short"
});
formatter.format(date);
// "Monday, January 15, 2024 at 2:30 PM"
第二种方法使用组件选项。这种方法可以对日期的每个部分进行精细控制。
const formatter = new Intl.DateTimeFormat("en-US", {
year: "numeric",
month: "long",
day: "numeric",
hour: "numeric",
minute: "numeric"
});
formatter.format(date);
// "January 15, 2024 at 2:30 PM"
不能将样式快捷方式与组件选项混合使用。每个格式化器只能选择一种方法。
样式快捷方式
dateStyle 和 timeStyle 选项提供了四种预设的格式化级别。
dateStyle 选项用于格式化日期部分。
const date = new Date(2024, 0, 15);
const full = new Intl.DateTimeFormat("en-US", { dateStyle: "full" });
full.format(date);
// "Monday, January 15, 2024"
const long = new Intl.DateTimeFormat("en-US", { dateStyle: "long" });
long.format(date);
// "January 15, 2024"
const medium = new Intl.DateTimeFormat("en-US", { dateStyle: "medium" });
medium.format(date);
// "Jan 15, 2024"
const short = new Intl.DateTimeFormat("en-US", { dateStyle: "short" });
short.format(date);
// "1/15/24"
timeStyle 选项用于格式化时间部分。
const date = new Date(2024, 0, 15, 14, 30, 45);
const full = new Intl.DateTimeFormat("en-US", { timeStyle: "full" });
full.format(date);
// "2:30:45 PM Eastern Standard Time"
const long = new Intl.DateTimeFormat("en-US", { timeStyle: "long" });
long.format(date);
// "2:30:45 PM EST"
const medium = new Intl.DateTimeFormat("en-US", { timeStyle: "medium" });
medium.format(date);
// "2:30:45 PM"
const short = new Intl.DateTimeFormat("en-US", { timeStyle: "short" });
short.format(date);
// "2:30 PM"
可以结合两个选项同时格式化日期和时间。
const formatter = new Intl.DateTimeFormat("en-US", {
dateStyle: "medium",
timeStyle: "short"
});
formatter.format(date);
// "Jan 15, 2024, 2:30 PM"
样式快捷方式会自动适应区域设置的约定。
const usFormatter = new Intl.DateTimeFormat("en-US", {
dateStyle: "short",
timeStyle: "short"
});
usFormatter.format(date);
// "1/15/24, 2:30 PM"
const deFormatter = new Intl.DateTimeFormat("de-DE", {
dateStyle: "short",
timeStyle: "short"
});
deFormatter.format(date);
// "15.1.24, 14:30"
德语格式使用点号作为分隔符,日期和月份顺序颠倒,并显示 24 小时制时间。美式格式使用斜杠,月份优先的顺序,以及带有 AM/PM 的 12 小时制时间。相同的选项,根据区域设置输出不同的结果。
组件选项
组件选项可以精确控制显示的内容和方式。每个选项都指定日期或时间的一部分。
year 选项用于显示年份。
const formatter = new Intl.DateTimeFormat("en-US", { year: "numeric" });
formatter.format(date);
// "2024"
const twoDigit = new Intl.DateTimeFormat("en-US", { year: "2-digit" });
twoDigit.format(date);
// "24"
month 选项以多种格式显示月份。
const numeric = new Intl.DateTimeFormat("en-US", { month: "numeric" });
numeric.format(date);
// "1"
const twoDigit = new Intl.DateTimeFormat("en-US", { month: "2-digit" });
twoDigit.format(date);
// "01"
const long = new Intl.DateTimeFormat("en-US", { month: "long" });
long.format(date);
// "January"
const short = new Intl.DateTimeFormat("en-US", { month: "short" });
short.format(date);
// "Jan"
const narrow = new Intl.DateTimeFormat("en-US", { month: "narrow" });
narrow.format(date);
// "J"
day 选项用于显示日期。
const formatter = new Intl.DateTimeFormat("en-US", { day: "numeric" });
formatter.format(date);
// "15"
const twoDigit = new Intl.DateTimeFormat("en-US", { day: "2-digit" });
twoDigit.format(date);
// "15"
weekday 选项用于显示星期几。
const long = new Intl.DateTimeFormat("en-US", { weekday: "long" });
long.format(date);
// "Monday"
const short = new Intl.DateTimeFormat("en-US", { weekday: "short" });
short.format(date);
// "Mon"
const narrow = new Intl.DateTimeFormat("en-US", { weekday: "narrow" });
narrow.format(date);
// "M"
通过组合多个组件选项,可以构建自定义的日期格式。
const formatter = new Intl.DateTimeFormat("en-US", {
weekday: "long",
year: "numeric",
month: "long",
day: "numeric"
});
formatter.format(date);
// "Monday, January 15, 2024"
浏览器会根据区域设置的惯例处理间距和标点符号。
时间格式化
时间组件选项控制小时、分钟和秒的显示。
const date = new Date(2024, 0, 15, 14, 30, 45);
const formatter = new Intl.DateTimeFormat("en-US", {
hour: "numeric",
minute: "numeric",
second: "numeric"
});
formatter.format(date);
// "2:30:45 PM"
hour12 选项控制 12 小时制和 24 小时制的时间显示。
const hour12 = new Intl.DateTimeFormat("en-US", {
hour: "numeric",
minute: "numeric",
hour12: true
});
hour12.format(date);
// "2:30 PM"
const hour24 = new Intl.DateTimeFormat("en-US", {
hour: "numeric",
minute: "numeric",
hour12: false
});
hour24.format(date);
// "14:30"
hourCycle 选项提供了更精细的小时显示控制。共有四种选项。
// h11: 0-11
const h11 = new Intl.DateTimeFormat("en-US", {
hour: "numeric",
hourCycle: "h11"
});
h11.format(new Date(2024, 0, 15, 0, 30));
// "0:30 AM"
// h12: 1-12
const h12 = new Intl.DateTimeFormat("en-US", {
hour: "numeric",
hourCycle: "h12"
});
h12.format(new Date(2024, 0, 15, 0, 30));
// "12:30 AM"
// h23: 0-23
const h23 = new Intl.DateTimeFormat("en-US", {
hour: "numeric",
hourCycle: "h23"
});
h23.format(new Date(2024, 0, 15, 0, 30));
// "0:30"
// h24: 1-24
const h24 = new Intl.DateTimeFormat("en-US", {
hour: "numeric",
hourCycle: "h24"
});
h24.format(new Date(2024, 0, 15, 0, 30));
// "24:30"
在午夜时分,差异尤为明显。h11 周期使用 0,而 h12 使用 12。同样,h23 使用 0,而 h24 使用 24。
dayPeriod 选项为 12 小时制时间添加描述性时间段标记。
const morning = new Date(2024, 0, 15, 10, 30);
const afternoon = new Date(2024, 0, 15, 14, 30);
const night = new Date(2024, 0, 15, 22, 30);
const formatter = new Intl.DateTimeFormat("en-US", {
hour: "numeric",
minute: "numeric",
dayPeriod: "long"
});
formatter.format(morning);
// "10:30 in the morning"
formatter.format(afternoon);
// "2:30 in the afternoon"
formatter.format(night);
// "10:30 at night"
dayPeriod 选项仅适用于 12 小时制时间格式。
fractionalSecondDigits 选项显示子秒精度。
const date = new Date(2024, 0, 15, 14, 30, 45, 123);
const oneDigit = new Intl.DateTimeFormat("en-US", {
hour: "numeric",
minute: "numeric",
second: "numeric",
fractionalSecondDigits: 1
});
oneDigit.format(date);
// "2:30:45.1 PM"
const threeDigits = new Intl.DateTimeFormat("en-US", {
hour: "numeric",
minute: "numeric",
second: "numeric",
fractionalSecondDigits: 3
});
threeDigits.format(date);
// "2:30:45.123 PM"
您可以指定一位、两位或三位小数位。
时区处理
timeZone 选项可将日期转换为特定时区。
const date = new Date("2024-01-15T14:30:00Z"); // UTC 时间
const newYork = new Intl.DateTimeFormat("en-US", {
timeZone: "America/New_York",
dateStyle: "full",
timeStyle: "long"
});
newYork.format(date);
// "Monday, January 15, 2024 at 9:30:00 AM EST"
const tokyo = new Intl.DateTimeFormat("ja-JP", {
timeZone: "Asia/Tokyo",
dateStyle: "full",
timeStyle: "long"
});
tokyo.format(date);
// "2024年1月15日月曜日 23:30:00 日本標準時"
const london = new Intl.DateTimeFormat("en-GB", {
timeZone: "Europe/London",
dateStyle: "full",
timeStyle: "long"
});
london.format(date);
// "Monday, 15 January 2024 at 14:30:00 GMT"
同一时刻在不同的时区显示不同的时间。使用 IANA 时区标识符,例如 America/New_York、Europe/London 或 Asia/Tokyo。
timeZoneName 选项可显示时区名称。
const formatter = new Intl.DateTimeFormat("en-US", {
hour: "numeric",
minute: "numeric",
timeZone: "America/New_York",
timeZoneName: "short"
});
formatter.format(date);
// "9:30 AM EST"
const long = new Intl.DateTimeFormat("en-US", {
hour: "numeric",
minute: "numeric",
timeZone: "America/New_York",
timeZoneName: "long"
});
long.format(date);
// "9:30 AM Eastern Standard Time"
const shortOffset = new Intl.DateTimeFormat("en-US", {
hour: "numeric",
minute: "numeric",
timeZone: "America/New_York",
timeZoneName: "shortOffset"
});
shortOffset.format(date);
// "9:30 AM GMT-5"
const longOffset = new Intl.DateTimeFormat("en-US", {
hour: "numeric",
minute: "numeric",
timeZone: "America/New_York",
timeZoneName: "longOffset"
});
longOffset.format(date);
// "9:30 AM GMT-05:00"
不同的 timeZoneName 值提供不同的详细程度。
日历系统和数字系统
calendar 选项支持非公历的日历系统。
const date = new Date(2024, 0, 15);
const gregorian = new Intl.DateTimeFormat("en-US", {
calendar: "gregory",
year: "numeric",
month: "long",
day: "numeric"
});
gregorian.format(date);
// "January 15, 2024"
const japanese = new Intl.DateTimeFormat("ja-JP", {
calendar: "japanese",
year: "numeric",
month: "long",
day: "numeric"
});
japanese.format(date);
// "令和6年1月15日"
const islamic = new Intl.DateTimeFormat("ar-SA", {
calendar: "islamic",
year: "numeric",
month: "long",
day: "numeric"
});
islamic.format(date);
// "٦ رجب ١٤٤٥"
const chinese = new Intl.DateTimeFormat("zh-CN", {
calendar: "chinese",
year: "numeric",
month: "long",
day: "numeric"
});
chinese.format(date);
// "2023甲辰年腊月初五"
同一日期可转换为不同的日历系统。可用的日历包括 gregory、japanese、islamic、islamic-umalqura、islamic-tbla、islamic-civil、islamic-rgsa、chinese、hebrew、indian、persian 等。
numberingSystem 选项可显示不同书写系统的数字。
const date = new Date(2024, 0, 15);
const western = new Intl.DateTimeFormat("en-US", {
numberingSystem: "latn",
year: "numeric",
month: "numeric",
day: "numeric"
});
western.format(date);
// "1/15/2024"
const arabic = new Intl.DateTimeFormat("en-US", {
numberingSystem: "arab",
year: "numeric",
month: "numeric",
day: "numeric"
});
arabic.format(date);
// "١/١٥/٢٠٢٤"
const devanagari = new Intl.DateTimeFormat("en-US", {
numberingSystem: "deva",
year: "numeric",
month: "numeric",
day: "numeric"
});
devanagari.format(date);
// "१/१५/२०२४"
const bengali = new Intl.DateTimeFormat("en-US", {
numberingSystem: "beng",
year: "numeric",
month: "numeric",
day: "numeric"
});
bengali.format(date);
// "১/১৫/২০২৪"
可用的数字系统包括 latn(西方数字)、arab(阿拉伯-印度数字)、arabext(扩展阿拉伯-印度数字)、beng(孟加拉数字)、deva(天城文数字)等。
格式化日期范围
formatRange() 方法通过智能省略冗余信息来格式化日期范围。
const start = new Date(2024, 0, 15);
const end = new Date(2024, 0, 20);
const formatter = new Intl.DateTimeFormat("en-US", {
year: "numeric",
month: "long",
day: "numeric"
});
formatter.formatRange(start, end);
// "2024年1月15日 – 20日"
格式化器会省略重复的信息。两个日期都在 2024 年 1 月,因此输出中只包含一次月份和年份。
当范围跨越不同月份时,会显示两个日期的月份。
const start = new Date(2024, 0, 15);
const end = new Date(2024, 1, 20);
formatter.formatRange(start, end);
// "2024年1月15日 – 2月20日"
当范围跨越不同年份时,会显示所有信息。
const start = new Date(2024, 0, 15);
const end = new Date(2025, 1, 20);
formatter.formatRange(start, end);
// "2024年1月15日 – 2025年2月20日"
formatRange() 方法也适用于时间范围。
const start = new Date(2024, 0, 15, 14, 30);
const end = new Date(2024, 0, 15, 16, 45);
const formatter = new Intl.DateTimeFormat("en-US", {
month: "long",
day: "numeric",
hour: "numeric",
minute: "numeric"
});
formatter.formatRange(start, end);
// "2024年1月15日,下午2:30 – 4:45"
由于两个时间都在同一天,因此日期只显示一次。
访问格式化的部分
formatToParts() 方法返回一个对象数组,每个对象表示格式化日期的一个部分。这使得自定义格式逻辑成为可能。
const date = new Date(2024, 0, 15, 14, 30);
const formatter = new Intl.DateTimeFormat("en-US", {
year: "numeric",
month: "long",
day: "numeric",
hour: "numeric",
minute: "numeric"
});
const parts = formatter.formatToParts(date);
数组中的每个对象都有一个 type 和 value 属性。
[
{ type: "month", value: "1月" },
{ type: "literal", value: " " },
{ type: "day", value: "15" },
{ type: "literal", value: "," },
{ type: "year", value: "2024" },
{ type: "literal", value: " " },
{ type: "hour", value: "2" },
{ type: "literal", value: ":" },
{ type: "minute", value: "30" },
{ type: "literal", value: " " },
{ type: "dayPeriod", value: "下午" }
]
您可以过滤和操作这些部分以构建自定义格式。
const dateParts = parts.filter(part =>
["month", "day", "year"].includes(part.type)
);
const dateString = dateParts.map(part => part.value).join("/");
// "1月/15/2024"
formatRangeToParts() 方法为日期范围提供了相同的功能。
const start = new Date(2024, 0, 15);
const end = new Date(2024, 0, 20);
const formatter = new Intl.DateTimeFormat("en-US", {
year: "numeric",
month: "long",
day: "numeric"
});
const parts = formatter.formatRangeToParts(start, end);
每个部分对象都包含一个 source 属性,指示它来自开始日期还是结束日期。
[
{ type: "month", value: "1月", source: "startRange" },
{ type: "literal", value: " ", source: "startRange" },
{ type: "day", value: "15", source: "startRange" },
{ type: "literal", value: " – ", source: "shared" },
{ type: "day", value: "20", source: "endRange" },
{ type: "literal", value: ",", source: "shared" },
{ type: "year", value: "2024", source: "shared" }
]
最佳实践
在使用相同选项格式化多个日期时,重用格式化器实例。创建格式化器涉及到区域设置协商和选项解析,这会有一定的性能开销。
// 效率较低
dates.forEach(date => {
const formatted = new Intl.DateTimeFormat("en-US").format(date);
console.log(formatted);
});
// 效率较高
const formatter = new Intl.DateTimeFormat("en-US");
dates.forEach(date => {
const formatted = formatter.format(date);
console.log(formatted);
});
尽可能使用用户浏览器的区域设置,通过省略区域设置参数来实现。这可以尊重用户的偏好。
const formatter = new Intl.DateTimeFormat();
// 自动使用浏览器的区域设置
在针对特定地区时提供备用区域设置。如果首选区域设置不可用,浏览器会使用下一个选项。
const formatter = new Intl.DateTimeFormat(["fr-CA", "fr", "en"]);
// 优先使用法语(加拿大),然后是法语,最后是英语
对于常见的日期和时间显示,使用样式快捷方式。它们会自动适应区域设置的约定,并且需要更少的配置。
const formatter = new Intl.DateTimeFormat("en-US", {
dateStyle: "medium",
timeStyle: "short"
});
当需要精确控制输出格式时,使用组件选项。
const formatter = new Intl.DateTimeFormat("en-US", {
weekday: "long",
year: "numeric",
month: "long",
day: "numeric"
});
在向远程用户显示时间或安排事件时,始终指定时区。如果没有指定时区,日期会以用户的本地时区格式化,这可能与您的意图不符。
const formatter = new Intl.DateTimeFormat("en-US", {
timeZone: "America/New_York",
dateStyle: "full",
timeStyle: "long"
});
使用 formatRange() 格式化日期范围,而不是分别格式化每个日期并将它们连接起来。该方法可以智能地省略冗余信息。
// 不够清晰
const startFormatted = formatter.format(start);
const endFormatted = formatter.format(end);
const range = `${startFormatted} to ${endFormatted}`;
// 更好
const range = formatter.formatRange(start, end);
检查浏览器对高级功能的兼容性,例如 dayPeriod、fractionalSecondDigits 和某些 timeZoneName 值。所有现代浏览器都支持核心功能,但较新的选项可能需要为旧浏览器提供备用方案。