JS原生实现日期格式化:不依赖库的代码示例
本文介绍在不依赖第三方库的情况下,使用原生JavaScript实现日期时间格式化的两种主流方案:基于正则替换与补零的自定义通用函数,以及现代标准API Intl.DateTimeFormat,并提供常见格式的直接可用代码与避坑指南。
在日常前端与 Node.js 开发中,展示格式化的日期(例如 YYYY-MM-DD HH:mm:ss)是一个极为普遍的需求。引入像 Day.js 或 Moment.js 这样的第三方库虽然省事,但会增加项目依赖和打包体积。现代 JavaScript 原生提供了足够完善的能力,开发者可以通过基于正则替换的轻量通用函数或官方标准的 Intl.DateTimeFormat API 两种途径,完全不依赖任何外部库实现各种格式的日期输出。
一、轻量通用的自定义格式化函数(模板替换法)
对于大部分常规业务场景,开发者最希望能够传入类似 "YYYY-MM-DD HH:mm:ss" 的模板字符串,并自动得到对应的结果。基于原生 Date 对象的 getter 方法配合 ES2017 引入的 String.prototype.padStart(),可以写出一个兼具可读性与通用性的轻量格式化函数。
1. 完整实现代码
/**
* 原生格式化日期函数
* @param {Date|number|string} dateInput - 可转换为有效日期的参数
* @param {string} pattern - 格式化模板,如 "YYYY-MM-DD HH:mm:ss"
* @returns {string} 格式化后的字符串
*/
function formatDate(dateInput = new Date(), pattern = 'YYYY-MM-DD HH:mm:ss') {
const date = dateInput instanceof Date ? dateInput : new Date(dateInput);
// 校验日期有效性
if (Number.isNaN(date.getTime())) {
throw new TypeError('Invalid Date: 传入的参数无法转换为有效日期');
}
const padZero = (num, length = 2) => String(num).padStart(length, '0');
const year = date.getFullYear();
const month = date.getMonth() + 1; // getMonth 返回 0-11,需加 1
const day = date.getDate();
const hours = date.getHours();
const minutes = date.getMinutes();
const seconds = date.getSeconds();
const milliseconds = date.getMilliseconds();
// 规则替换映射表(长标记优先匹配,避免被短标记提前替换)
const tokenMap = {
YYYY: String(year),
YY: String(year).slice(-2),
MM: padZero(month),
M: String(month),
DD: padZero(day),
D: String(day),
HH: padZero(hours),
H: String(hours),
mm: padZero(minutes),
m: String(minutes),
ss: padZero(seconds),
s: String(seconds),
SSS: padZero(milliseconds, 3),
};
// 生成正则:匹配所有支持的 token
const regex = /YYYY|YY|MM|M|DD|D|HH|H|mm|m|ss|s|SSS/g;
return pattern.replace(regex, (match) => tokenMap[match]);
}
// 调用示例
const now = new Date('2026-11-10T14:05:09.042');
console.log(formatDate(now, 'YYYY-MM-DD HH:mm:ss'));
console.log(formatDate(now, 'YYYY/MM/DD'));
console.log(formatDate(now, 'YY年M月D日 HH:mm:ss.SSS'));
console.log(formatDate(now, 'HH:mm'));
2. 执行逻辑拆解
该函数的执行流程遵循清晰的数据流水线:从入参类型统一转化,到各维度时间提取,再到字典映射与正则匹配输出。
二、使用原生标准 API:Intl.DateTimeFormat
如果项目要求遵循 ECMAScript 国际化规范、需要处理特定语言环境(如中文“2026年11月10日 星期二”)或特定时区转换,推荐使用浏览器与 Node.js 均内置的 Intl.DateTimeFormat。
1. 基础用法:配置 Options
// 针对中文环境的基础格式化
const formatterCN = new Intl.DateTimeFormat('zh-CN', {
year: 'numeric',
month: '2-digit',
day: '2-digit',
hour: '2-digit',
minute: '2-digit',
second: '2-digit',
hour12: false, // 24小时制
});
const targetDate = new Date('2026-11-10T14:30:45');
console.log(formatterCN.format(targetDate));
2. 进阶用法:利用 formatToParts 自由组装任意格式
format() 方法生成的连接符(如斜杠或短横线)受特定系统和区域规则约束。如果必须强制输出 YYYY-MM-DD HH:mm:ss 格式,可以调用 formatToParts(),它会将时间拆解成结构化对象数组,再由开发者自由拼接。
/**
* 基于 Intl.DateTimeFormat 封装的精准格式化函数
* @param {Date} date - 目标日期
* @param {string} timeZone - 目标时区,默认为运行环境本地时区
* @returns {string} 格式为 "YYYY-MM-DD HH:mm:ss" 的字符串
*/
function formatWithIntlParts(date = new Date(), timeZone = Intl.DateTimeFormat().resolvedOptions().timeZone) {
const formatter = new Intl.DateTimeFormat('en-US', {
timeZone,
year: 'numeric',
month: '2-digit',
day: '2-digit',
hour: '2-digit',
minute: '2-digit',
second: '2-digit',
hour12: false,
});
const parts = formatter.formatToParts(date);
const partMap = {};
// 将 parts 数组转化为键值对
for (const { type, value } of parts) {
if (type !== 'literal') {
partMap[type] = value;
}
}
// 强制组装目标格式
return `${partMap.year}-${partMap.month}-${partMap.day} ${partMap.hour}:${partMap.minute}:${partMap.second}`;
}
console.log(formatWithIntlParts(new Date('2026-11-10T08:09:10')));
// 指定时区输出(例如东京时间 UTC+9)
console.log(formatWithIntlParts(new Date('2026-11-10T08:09:10Z'), 'Asia/Tokyo'));
三、高频场景的极简原生片段(即拷即用)
在某些轻量场景下,为了极短的代码量,开发者往往只需要单个特定格式。以下是几种常见模式的原生写法:
1. 仅提取当前日期的 YYYY-MM-DD(本地时间安全版)
function getLocalDateString(d = new Date()) {
const pad = (n) => String(n).padStart(2, '0');
return `${d.getFullYear()}-${pad(d.getMonth() + 1)}-${pad(d.getDate())}`;
}
console.log(getLocalDateString()); // "2026-11-10"
注意:避免使用 new Date().toISOString().slice(0, 10) 来获取“今天”的日期。因为 toISOString() 固定输出 UTC 零时区时间。对于东八区(UTC+8)的用户,在早晨 00:00 至 07:59 期间调用该方法,会得到昨天的日期,导致日期漂移Bug。
2. 快速生成中文“年月日 星期X”
function formatChineseDate(date = new Date()) {
return new Intl.DateTimeFormat('zh-CN', {
year: 'numeric',
month: 'long',
day: 'numeric',
weekday: 'long',
}).format(date);
}
console.log(formatChineseDate(new Date('2026-11-10')));
// 输出示例:"2026年11月10日 星期二"
四、原生日期处理的三个关键陷阱
JavaScript 原生 Date 对象设计源自早期的 Java 1.0 API,存在诸多历史遗留特征。在编写原生格式化逻辑时,需要重点避开以下三点:
五、方案选型与对比建议
为了在实际工程中做出恰当的技术选型,可根据场景复杂度参考下述对比矩阵:
| 考量维度 | 自定义模板函数(formatDate) | Intl.DateTimeFormat | 第三方库(Day.js / date-fns) |
|---|---|---|---|
| 引入成本 | 0 依赖,直接嵌入工具函数(< 1KB) | 0 依赖,原生标准内置(0KB) | 需 npm 安装,打包体积 2KB ~ 20KB+ |
| 模板自由度 | 极高(完全受控于 pattern 占位符) | 中(需通过 formatToParts 手动拼接) | 极高(链式调用,API丰富) |
| 国际化与本地化 | 较弱(需手动维护语言映射表) | 极强(支持数百种语言、星期、月份全称) | 较强(需单独加载对应 locale 包) |
| 时区转换支持 | 需手动计算 UTC offset | 原生支持 timeZone 参数转换 |
需加载 timezone 插件 |
| 推荐使用场景 | 常规业务、后台管理系统、日期展示 | 跨国多语言界面、精准时区展示 | 极复杂的日期加减运算、业务倒计时库 |
综上所述,在现代前端开发中,除非项目有密集的日期算术计算(如链式日期加减、复杂的周期重合计算),绝大多数针对日期展示和格式化的需求,完全可以用不到 30 行的原生代码或标准的 Intl API 高效解决,无需引入沉重的外部包。
封装一个精准的JS四舍五入函数
2026-11-11 15:40:24
JS怎么处理含单引号或非标准格式的JSON字符串
2026-11-11 14:28:14
dw怎么用javascript的命名和写法规范怎么定
2026-11-08 22:18:41
js函数里面的data参数怎么解决小数精度问题
2026-11-08 21:26:37
JavaScript框架库漏洞怎么做查找和替换
2026-11-08 19:04:29








