日期格式新利器 - Intl.DateTimeFormat
作为一名前端工程师, 我们在工作中经常会遇到对日期的格式化处理的需求,比如将 new Date.now() 格式化成 YYYY年 MM月 DD 日 HH时MM分SS秒,又或者将比如 2023年1月12日 格式化成 癸卯年正月十二星期日。
这看起来似乎也不算很难,我们可以借助 moment.js 或者 dayjs 等相关的日期格式化的库解决这类问题,毕竟 JS 内置的 Date 对象在日期格式化并没有提供内置的 API,大部分都需要调用库来实现。
但引入库也是有格外的 bundle 开销的,像 momentjs 这类体积过于庞大且无法 tree-shake 的库,已经淡出了开发者的视线。幸好我们迎来了 dayjs 这类轻巧的(不安装格外的插件下打包最小 2kb)且支持 immutable 以及 tree-shake 的插件,看起来已经很完美了。
其实,有个可能鲜为人知的内置日期对象早在 2012 年提供对日期的灵活转换了,那就是 Intl.DateTimeFormat。由于刚出来的时候兼容性不佳且有很多 API 还未完善,很少有人提起过。
但是在 2023 年的今天,如果你不考虑兼容太过老旧的浏览器的话,大可放心使用 Intl.DateTimeFormat。
认识 Intl.DateTimeFormat
Intl.DateTimeFormat 对象能使日期和时间在特定的语言环境下格式化。
Intl.DateTimeFormat 函数需要通过 new 关键字构造调用并返回一个 Intl.DateTimeFormat 对象。
其实也可以不需要通过 new 关键字构造调用, 但不使用 new 会导致非预期的行为,例如 this 可能会指向另一个Intl.DateTimeFormat。
Intl.DateTimeFormat构造函数接收两个参数,这两个参数都是可选的。
第一个参数是 locales, 可以理解为需要 format 的日期语言是哪个国家的语言, locales 可以是一个 Array<string> 也可以是 string。
// 如果是一个数组,则意味着指定多个 fallback language
// 在本例中,如果指定的日期内容无法被`zh-Hant`匹配,则继续往左匹配 `zh`,如果 `zh` 也没有匹配到,则会使用默认值
// match-pattern 可以通过 https://datatracker.ietf.org/doc/html/rfc4647#section-3.4 查阅
Intl.DateTimeFormat(["zh", "zh-Hant"]);
Intl.DateTimeFormat("zh");
第二个参数 options 给定一个具有多个属性的对象,用于指定如何格式给定的日期内容。
接下来一起看看有哪些可用的属性。
dateStyle
dateStyle属性指定调用 format() 时使用的日期格式样式,其枚举值如下:
full, 格式的日期内容包含年月日以及本周的星期几long, 格式的日期内容包含年月日medium,格式的日期内容包含年月日(如果locale为en-US等英语语言,则月份的单词是简写形式,例如February会被输出Feb)short,格式的日期内容包含年月日(拼接日期的单位会被简写符号替代,例如/,-)
示例
// 输出 2023年2月12日星期日
new Intl.DateTimeFormat("zh", { dateStyle: "full" }).format();
// 输出 2023年2月12日
new Intl.DateTimeFormat("zh", { dateStyle: "long" }).format();
// 输出 2023年2月12日
new Intl.DateTimeFormat("zh", { dateStyle: "medium" }).format();
// `long` 和 `medium`在英语中书写有区别
// February 12, 2023
new Intl.DateTimeFormat("en-US", { dateStyle: "long" }).format();
// Feb 12, 2023
new Intl.DateTimeFormat("en-US", { dateStyle: "medium" }).format();
// 2023/2/12 (short不会拼接日期单位)
new Intl.DateTimeFormat("zh", { dateStyle: "short" }).format();
timeStyle
timeStyle属性指定调用 format() 时使用的时间格式样式,其枚举值如下:
full,格式的时间内容包含国家,时,分,秒long, 格式的时间内容包含时区,时,分,秒medium,格式的时间内容包含时,分,秒short,格式的时间内容包含时,分
示例
// 中国标准时间 17:37:16
new Intl.DateTimeFormat("zh", { timeStyle: "full" }).format();
// GMT+8 17:37:21
new Intl.DateTimeFormat("zh", { timeStyle: "long" }).format();
// 17:37:37
new Intl.DateTimeFormat("zh", { timeStyle: "medium" }).format();
// 17:37
new Intl.DateTimeFormat("zh", { timeStyle: "short" }).format();
// 5:38:02 PM GMT+8
new Intl.DateTimeFormat("en-US", { timeStyle: "long" }).format();
// 5:37:58 PM
new Intl.DateTimeFormat("en-US", { timeStyle: "medium" }).format();
// 5:37 PM
new Intl.DateTimeFormat("en-US", { timeStyle: "short" }).format();
dateStyle 可以与 timeStyle 一起使用,但不能与其他配置项(例如week、hour、month等)一起使用( timeStyle 同样如此 )。
calendar
calendar属性指定调用 format() 时使用的本地国家传统日历格式样式,例如中国有农历甲子年。
可取的值如下:
"buddhist", "chinese", "coptic", "dangi", "ethioaa", "ethiopic", "gregory", "hebrew", "indian", "islamic", "islamic-umalqura", "islamic-tbla", "islamic-civil", "islamic-rgsa", "iso8601", "japanese", "persian", "roc", "islamicc".
// 2023癸卯年正月廿二星期日
new Intl.DateTimeFormat("zh", {
dateStyle: "short",
calendar: "chinese",
}).format();
dayPeriod
dayPeriod属性指定调用 format() 时使用的时间段。例如 上午、下午。
dayPeriod 可枚举的值有:narrow, short, short,不过在中文和英语中,这三个值毫无区别。
// 下午
new Intl.DateTimeFormat("zh", { dayPeriod: "long" }).format();
// in the evening
new Intl.DateTimeFormat("en-US", { dayPeriod: "long" }).format();
dayPeriod 无法和 dateStyle 或 timeStyle 一同使用。
hour12
hour12属性指定调用 format() 时是否使用的 12 小时制来表示时间。可枚举的值有: true、false。
// 下午06:55:26
new Intl.DateTimeFormat("zh", { timeStyle: "medium", hour12: true }).format();
// 6:57:19 PM
new Intl.DateTimeFormat("en-US", {
timeStyle: "medium",
hour12: true,
}).format();
weekday,year,month,day
前面说过 timeStyle 和 dateStyle 是不能与 weekday,year等属性一起使用的,那么这些属性的用途是什么呢?
回顾上文中的各种示例,我们都是围绕日期与时间进行格式化操作,其中日期最小的格式化是缩小到了年、月和日。
假如我们只需要格式化 月 或者 年,周这种最细粒度的日期单位的话,这些属性就能派上用场了。
我在下面的表格中列出来了在格式化输出中使用的日期和时间格式以及允许值
| Property | Values |
|---|---|
| weekday | narrow, short, long |
| year | 2-digit, numeric |
| month | 2-digit, numeric, narrow, short, long |
| day | 2-digit, numeric |
| hour | 2-digit, numeric |
| minute | 2-digit, numeric |
| second | 2-digit, numeric |
2-digit表示两位数表示法-
// 输出 02月
new Intl.DateTimeFormat("zh", { month: "2-digit" }).format();
-
numeric一位数表示法-
// 输出 2月
new Intl.DateTimeFormat("zh", { month: "2-digit" }).format();
-
short- 日期的简写,在英语语言中,例如 weekday 中
Friday会格式成Fri - 在中文语言中,星期日 会被格式成
周日
- 日期的简写,在英语语言中,例如 weekday 中
long- 日期的完整书写,在英语语言中,例如 weekday 中
Friday会被完整格式成Friday - 在中文语言中,例如星期日会被完整格式成
星期日
- 日期的完整书写,在英语语言中,例如 weekday 中
narrow- 日期的最短简写,在英语语言中,例如 weekday 中
Friday会取单词第一个字母格式成F - 在中文语言中,例如星期日会被完整格式成
日
- 日期的最短简写,在英语语言中,例如 weekday 中
更多的属性以及允许值我们可以从 tc39 中 proposal-intl-datetime-style 一章节查阅