日期时间格式化
日期时间格式化
Intl.DateTimeFormat对象用于格式化日期和时间,支持各种语言和地区的格式。本文将详细介绍DateTimeFormat的选项和用法,帮助您实现灵活的日期时间本地化。
Intl.DateTimeFormat 基础
Intl.DateTimeFormat
是 ECMAScript 国际化 API 的一部分,专门用于根据语言和地区的规则格式化日期和时间。它提供了丰富的选项,可以满足各种日期时间格式化需求。
基本语法
new Intl.DateTimeFormat([locales[, options]])
locales
:可选,指定一个或多个语言标签(如 'zh-CN'、'en-US')options
:可选,一个对象,包含各种格式化选项
简单示例
// 使用默认区域设置格式化当前日期
const date = new Date();
const formatter = new Intl.DateTimeFormat();
console.log(formatter.format(date)); // 例如:2023/4/1
// 使用特定区域设置
const cnFormatter = new Intl.DateTimeFormat('zh-CN');
console.log(cnFormatter.format(date)); // 例如:2023/4/1
const usFormatter = new Intl.DateTimeFormat('en-US');
console.log(usFormatter.format(date)); // 例如:4/1/2023
格式化选项
Intl.DateTimeFormat
提供了丰富的选项来自定义日期和时间的格式。
日期组件选项
可以单独指定日期的各个部分的格式:
const date = new Date(2023, 3, 15, 20, 30, 45);
const formatter = new Intl.DateTimeFormat('zh-CN', {
year: 'numeric', // 'numeric', '2-digit'
month: 'long', // 'numeric', '2-digit', 'narrow', 'short', 'long'
day: 'numeric', // 'numeric', '2-digit'
weekday: 'long', // 'narrow', 'short', 'long'
});
console.log(formatter.format(date)); // 例如:2023年4月15日星期六
时间组件选项
同样可以指定时间的各个部分的格式:
const date = new Date(2023, 3, 15, 20, 30, 45);
const formatter = new Intl.DateTimeFormat('zh-CN', {
hour: 'numeric', // 'numeric', '2-digit'
minute: 'numeric', // 'numeric', '2-digit'
second: 'numeric', // 'numeric', '2-digit'
hour12: true, // true 使用12小时制,false 使用24小时制
});
console.log(formatter.format(date)); // 例如:晚上8:30:45
组合日期和时间
可以同时格式化日期和时间:
const date = new Date(2023, 3, 15, 20, 30, 45);
const formatter = new Intl.DateTimeFormat('zh-CN', {
year: 'numeric',
month: 'long',
day: 'numeric',
weekday: 'long',
hour: 'numeric',
minute: 'numeric',
second: 'numeric',
hour12: true,
timeZone: 'Asia/Shanghai'
});
console.log(formatter.format(date)); // 例如:2023年4月15日星期六 晚上8:30:45
使用 dateStyle 和 timeStyle
ES2020 引入了 dateStyle
和 timeStyle
选项,提供了更简洁的方式来指定日期和时间的格式:
const date = new Date(2023, 3, 15, 20, 30, 45);
// 只格式化日期
const dateFormatter = new Intl.DateTimeFormat('zh-CN', {
dateStyle: 'full' // 'full', 'long', 'medium', 'short'
});
console.log(dateFormatter.format(date)); // 例如:2023年4月15日星期六
// 只格式化时间
const timeFormatter = new Intl.DateTimeFormat('zh-CN', {
timeStyle: 'medium' // 'full', 'long', 'medium', 'short'
});
console.log(timeFormatter.format(date)); // 例如:晚上8:30:45
// 同时格式化日期和时间
const fullFormatter = new Intl.DateTimeFormat('zh-CN', {
dateStyle: 'full',
timeStyle: 'long'
});
console.log(fullFormatter.format(date)); // 例如:2023年4月15日星期六 UTC+8 晚上8:30:45
高级选项
时区设置
可以指定特定的时区:
const date = new Date();
// 使用不同时区
const nyFormatter = new Intl.DateTimeFormat('zh-CN', {
timeZone: 'America/New_York',
dateStyle: 'full',
timeStyle: 'long'
});
const tokyoFormatter = new Intl.DateTimeFormat('zh-CN', {
timeZone: 'Asia/Tokyo',
dateStyle: 'full',
timeStyle: 'long'
});
console.log('纽约时间:', nyFormatter.format(date));
console.log('东京时间:', tokyoFormatter.format(date));
日历系统
可以指定不同的日历系统:
const date = new Date();
// 使用不同日历系统
const gregorianFormatter = new Intl.DateTimeFormat('zh-CN', {
calendar: 'gregory',
dateStyle: 'full'
});
const chineseFormatter = new Intl.DateTimeFormat('zh-CN', {
calendar: 'chinese',
dateStyle: 'full'
});
const islamicFormatter = new Intl.DateTimeFormat('zh-CN', {
calendar: 'islamic',
dateStyle: 'full'
});
console.log('公历:', gregorianFormatter.format(date));
console.log('农历:', chineseFormatter.format(date));
console.log('伊斯兰历:', islamicFormatter.format(date));
数字系统
可以指定不同的数字系统:
const date = new Date();
// 使用不同数字系统
const latinFormatter = new Intl.DateTimeFormat('ar-EG', {
numberingSystem: 'latn', // 拉丁数字
dateStyle: 'full'
});
const arabicFormatter = new Intl.DateTimeFormat('ar-EG', {
numberingSystem: 'arab', // 阿拉伯数字
dateStyle: 'full'
});
console.log('拉丁数字:', latinFormatter.format(date));
console.log('阿拉伯数字:', arabicFormatter.format(date));
格式化方法
format()
最基本的格式化方法,返回格式化后的字符串:
const date = new Date();
const formatter = new Intl.DateTimeFormat('zh-CN', { dateStyle: 'full' });
console.log(formatter.format(date)); // 例如:2023年4月15日星期六
formatToParts()
返回一个数组,包含格式化后的各个部分:
const date = new Date(2023, 3, 15);
const formatter = new Intl.DateTimeFormat('zh-CN', {
year: 'numeric',
month: 'long',
day: 'numeric'
});
const parts = formatter.formatToParts(date);
console.log(parts);
// 输出类似:
// [
// { type: 'year', value: '2023' },
// { type: 'literal', value: '年' },
// { type: 'month', value: '4' },
// { type: 'literal', value: '月' },
// { type: 'day', value: '15' },
// { type: 'literal', value: '日' }
// ]
// 可以用于自定义格式
const year = parts.find(part => part.type === 'year').value;
const month = parts.find(part => part.type === 'month').value;
const day = parts.find(part => part.type === 'day').value;
console.log(`自定义格式:${year}/${month}/${day}`); // 自定义格式:2023/4/15
formatRange()
ES2021 引入的方法,用于格式化日期范围:
const start = new Date(2023, 3, 15);
const end = new Date(2023, 3, 20);
const formatter = new Intl.DateTimeFormat('zh-CN', {
year: 'numeric',
month: 'long',
day: 'numeric'
});
console.log(formatter.formatRange(start, end)); // 例如:2023年4月15日–20日
formatRangeToParts()
类似于 formatToParts()
,但用于日期范围:
const start = new Date(2023, 3, 15);
const end = new Date(2023, 3, 20);
const formatter = new Intl.DateTimeFormat('zh-CN', {
year: 'numeric',
month: 'long',
day: 'numeric'
});
const parts = formatter.formatRangeToParts(start, end);
console.log(parts);
// 输出类似:
// [
// { type: 'year', value: '2023', source: 'startRange' },
// { type: 'literal', value: '年', source: 'startRange' },
// { type: 'month', value: '4', source: 'startRange' },
// { type: 'literal', value: '月', source: 'startRange' },
// { type: 'day', value: '15', source: 'startRange' },
// { type: 'literal', value: '日', source: 'startRange' },
// { type: 'literal', value: '–', source: 'shared' },
// { type: 'day', value: '20', source: 'endRange' },
// { type: 'literal', value: '日', source: 'endRange' }
// ]
实际应用示例
创建日期选择器
function createDateOptions(locale) {
const formatter = new Intl.DateTimeFormat(locale, { month: 'long' });
const months = [];
// 生成月份选项
for (let i = 0; i < 12; i++) {
const date = new Date(2023, i, 1);
months.push({
value: i,
label: formatter.format(date)
});
}
return {
months
};
}
// 使用示例
const options = createDateOptions('zh-CN');
console.log(options.months);
// 输出类似:
// [
// { value: 0, label: '一月' },
// { value: 1, label: '二月' },
// ...
// ]
相对日期格式化
结合 Intl.RelativeTimeFormat
和 Intl.DateTimeFormat
创建更友好的日期显示:
function formatDate(date, locale) {
const now = new Date();
const diffInDays = Math.floor((date - now) / (1000 * 60 * 60 * 24));
// 如果是最近的日期,使用相对时间
if (Math.abs(diffInDays) < 7) {
const rtf = new Intl.RelativeTimeFormat(locale, { numeric: 'auto' });
return rtf.format(diffInDays, 'day');
}
// 否则使用标准日期格式
const thisYear = now.getFullYear() === date.getFullYear();
const options = thisYear
? { month: 'long', day: 'numeric' }
: { year: 'numeric', month: 'long', day: 'numeric' };
return new Intl.DateTimeFormat(locale, options).format(date);
}
// 使用示例
const yesterday = new Date();
yesterday.setDate(yesterday.getDate() - 1);
const lastWeek = new Date();
lastWeek.setDate(lastWeek.getDate() - 10);
const lastYear = new Date();
lastYear.setFullYear(lastYear.getFullYear() - 1);
console.log(formatDate(yesterday, 'zh-CN')); // 昨天
console.log(formatDate(lastWeek, 'zh-CN')); // 4月5日(假设今天是4月15日)
console.log(formatDate(lastYear, 'zh-CN')); // 2022年4月15日
多语言日期时间选择器
function createDateTimeUI(locale) {
// 创建月份选择器
const monthFormatter = new Intl.DateTimeFormat(locale, { month: 'long' });
const monthSelect = document.createElement('select');
for (let i = 0; i < 12; i++) {
const option = document.createElement('option');
option.value = i;
option.textContent = monthFormatter.format(new Date(2023, i, 1));
monthSelect.appendChild(option);
}
// 创建星期几标签
const weekdayFormatter = new Intl.DateTimeFormat(locale, { weekday: 'short' });
const weekdayLabels = [];
// 获取一周的第一天(根据区域设置可能不同)
const firstDay = new Intl.Locale(locale).weekInfo?.firstDay || 1;
for (let i = 0; i < 7; i++) {
const day = (i + firstDay) % 7;
// 创建一个日期对象,确保是星期几
const date = new Date(2023, 0, day + 1);
weekdayLabels.push(weekdayFormatter.format(date));
}
return {
monthSelect,
weekdayLabels
};
}
// 使用示例(在浏览器环境中)
// const ui = createDateTimeUI('zh-我将继续完成多语言日期时间选择器的示例代码,并添加更多实用的日期时间格式化示例:
```markdown:/Users/guanrunbai/my/kphub/src/frontend/JavaScript/01-基础指南/1.16-国际化/1.16.2-日期时间格式化.md
// const ui = createDateTimeUI('zh-CN');
// document.body.appendChild(ui.monthSelect);
// console.log('星期标签:', ui.weekdayLabels); // ['周一', '周二', '周三', '周四', '周五', '周六', '周日']
智能日期格式化
根据日期的不同情况自动选择最合适的格式:
function smartDateFormat(date, locale = 'zh-CN') {
const now = new Date();
const diffMs = date.getTime() - now.getTime();
const diffSec = Math.floor(diffMs / 1000);
const diffMin = Math.floor(diffSec / 60);
const diffHour = Math.floor(diffMin / 60);
const diffDay = Math.floor(diffHour / 24);
// 相对时间格式化器
const rtf = new Intl.RelativeTimeFormat(locale, { numeric: 'auto' });
// 1. 不到1分钟
if (Math.abs(diffSec) < 60) {
return rtf.format(diffSec, 'second');
}
// 2. 不到1小时
if (Math.abs(diffMin) < 60) {
return rtf.format(diffMin, 'minute');
}
// 3. 不到1天
if (Math.abs(diffHour) < 24) {
return rtf.format(diffHour, 'hour');
}
// 4. 不到7天
if (Math.abs(diffDay) < 7) {
return rtf.format(diffDay, 'day');
}
// 5. 同一年内
if (date.getFullYear() === now.getFullYear()) {
return new Intl.DateTimeFormat(locale, {
month: 'long',
day: 'numeric'
}).format(date);
}
// 6. 不同年
return new Intl.DateTimeFormat(locale, {
year: 'numeric',
month: 'long',
day: 'numeric'
}).format(date);
}
// 使用示例
const examples = [
new Date(Date.now() - 30 * 1000), // 30秒前
new Date(Date.now() - 10 * 60 * 1000), // 10分钟前
new Date(Date.now() - 5 * 60 * 60 * 1000), // 5小时前
new Date(Date.now() - 2 * 24 * 60 * 60 * 1000), // 2天前
new Date(Date.now() - 20 * 24 * 60 * 60 * 1000), // 20天前
new Date(Date.now() - 365 * 24 * 60 * 60 * 1000) // 1年前
];
examples.forEach(date => {
console.log(smartDateFormat(date, 'zh-CN'));
});
// 输出类似:
// 30秒前
// 10分钟前
// 5小时前
// 2天前
// 3月26日(假设今天是4月15日)
// 2022年4月15日
处理特殊日期格式
ISO 8601 格式转换
将 ISO 8601 格式的日期字符串转换为本地化格式:
function formatISODate(isoString, locale, options = {}) {
const date = new Date(isoString);
return new Intl.DateTimeFormat(locale, options).format(date);
}
// 使用示例
const isoDate = '2023-04-15T14:30:45.123Z';
console.log(formatISODate(isoDate, 'zh-CN', {
dateStyle: 'full',
timeStyle: 'long',
timeZone: 'Asia/Shanghai'
}));
// 输出类似:2023年4月15日星期六 UTC+8 下午10:30:45
自定义日期格式模板
创建一个函数,根据模板字符串格式化日期:
function formatDateWithTemplate(date, locale, template) {
const formatter = new Intl.DateTimeFormat(locale, {
year: 'numeric',
month: 'numeric',
day: 'numeric',
hour: 'numeric',
minute: 'numeric',
second: 'numeric',
weekday: 'long',
hour12: false,
timeZone: 'UTC'
});
const parts = formatter.formatToParts(date);
// 创建一个查找表
const partValues = {};
for (const part of parts) {
partValues[part.type] = part.value;
}
// 替换模板中的占位符
return template
.replace(/{year}/g, partValues.year || '')
.replace(/{month}/g, partValues.month || '')
.replace(/{day}/g, partValues.day || '')
.replace(/{weekday}/g, partValues.weekday || '')
.replace(/{hour}/g, partValues.hour || '')
.replace(/{minute}/g, partValues.minute || '')
.replace(/{second}/g, partValues.second || '');
}
// 使用示例
const date = new Date(2023, 3, 15, 14, 30, 45);
console.log(formatDateWithTemplate(
date,
'zh-CN',
'今天是 {year}年{month}月{day}日 {weekday},时间是 {hour}:{minute}:{second}'
));
// 输出:今天是 2023年4月15日 星期六,时间是 14:30:45
性能优化
缓存格式化器实例
为了提高性能,可以缓存 Intl.DateTimeFormat
实例:
// 创建格式化器缓存
const formatterCache = new Map();
function getCachedFormatter(locale, options) {
// 创建缓存键
const key = locale + JSON.stringify(options || {});
// 检查缓存
if (!formatterCache.has(key)) {
formatterCache.set(key, new Intl.DateTimeFormat(locale, options));
}
return formatterCache.get(key);
}
// 使用缓存的格式化器
function formatDateCached(date, locale, options) {
const formatter = getCachedFormatter(locale, options);
return formatter.format(date);
}
// 性能测试
function performanceTest() {
const date = new Date();
const options = { dateStyle: 'full', timeStyle: 'long' };
const iterations = 1000;
console.time('不缓存');
for (let i = 0; i < iterations; i++) {
new Intl.DateTimeFormat('zh-CN', options).format(date);
}
console.timeEnd('不缓存');
console.time('使用缓存');
for (let i = 0; i < iterations; i++) {
formatDateCached(date, 'zh-CN', options);
}
console.timeEnd('使用缓存');
}
// performanceTest();
// 输出类似:
// 不缓存: 150ms
// 使用缓存: 5ms
浏览器兼容性
Intl.DateTimeFormat
在现代浏览器中得到了广泛支持,但不同的浏览器和版本对特定功能的支持可能有所不同:
- 基本的
Intl.DateTimeFormat
功能在所有现代浏览器中都有良好支持 dateStyle
和timeStyle
选项在较新的浏览器版本中支持formatRange
和formatRangeToParts
方法是较新的添加,可能需要检查兼容性
可以使用特性检测来确保代码在不支持某些功能的浏览器中仍能正常工作:
function formatDateRange(start, end, locale, options) {
const formatter = new Intl.DateTimeFormat(locale, options);
// 检查是否支持 formatRange
if (typeof formatter.formatRange === 'function') {
return formatter.formatRange(start, end);
}
// 回退方案:分别格式化两个日期
return `${formatter.format(start)} - ${formatter.format(end)}`;
}
总结
Intl.DateTimeFormat
是一个强大的工具,用于根据不同语言和地区的规则格式化日期和时间。它提供了丰富的选项和方法,可以满足各种日期时间格式化需求。
主要优点包括:
- 国际化支持:自动处理不同语言和地区的日期时间格式
- 灵活的格式化选项:可以自定义日期和时间的各个部分的格式
- 日期范围格式化:支持格式化日期范围,自动处理共享部分
- 多种日历系统:支持多种日历系统,如公历、农历、伊斯兰历等
- 时区支持:可以指定不同的时区
通过掌握 Intl.DateTimeFormat
,开发者可以轻松实现多语言日期时间格式化,为全球用户提供本地化的体验。随着 Web 应用程序的全球化,Intl.DateTimeFormat
将成为前端开发者工具箱中越来越重要的工具。