在 JavaScript 中获取当前日期并格式化为 YYYY-MM-DD 的实用教程。详细讲解基于原生 Date API 的标准实现、两位补零对齐逻辑、常见的 toISOString 时区偏差误区,以及现代 Intl API 的优雅方案与避坑清单。
JS获取当前日期并格式化为YYYY-MM-DD
时间:2026-11-13
浏览:101
文章目录
适用场景:表单默认日期、请求参数拼接、文件命名、数据筛选与存储标记
YYYY-MM-DD(例如 2026-11-10),最稳妥、零依赖的标准做法是:通过 new Date() 实例化本地时间,分别使用 getFullYear()、getMonth() + 1 和 getDate() 提取年、月、日数值,再配合 ES2017 的 padStart(2, '0') 方法对个位数进行前导补零,最后用模板字面量拼接完成。
一、核心做法:现代原生 JavaScript 标准方案
在实际项目开发中,将日期规整为十位长度且按连字符分隔的 YYYY-MM-DD 格式是高频需求。无论是调用后端接口提交查询起止时间,还是在 HTML 日期输入框(<input type="date">)中设置初始值,都需要精确的本地年月日字符串。
推荐封装如下的标准函数,无需引入任何外部库,逻辑透明、兼容性极佳且完全杜绝时区干扰:
/**
* 获取当前本地日期并格式化为 YYYY-MM-DD
* @returns {string} 格式化后的日期字符串,例如 "2026-11-10"
*/
function getCurrentDateYMD() {
const now = new Date();
const year = now.getFullYear();
const month = String(now.getMonth() + 1).padStart(2, '0');
const day = String(now.getDate()).padStart(2, '0');
return `${year}-${month}-${day}`;
}
// 调用示例
const today = getCurrentDateYMD();
console.log(today);
关键步骤与实现原理解析
上述代码由四个清晰的逻辑步骤组成:
-
获取 4 位完整公历年份(
now.getFullYear()):该方法返回包含纪元的 4 位整数(例如 2026)。需要特别注意,不能使用早已废弃的getYear(),后者返回的是“当前年份减去 1900”的差值(在 2026 年会返回 126),容易引起低级错误。 -
提取月份并加 1(
now.getMonth() + 1):这是 JavaScriptDate对象设计中最易混淆的机制——月份的索引范围是0 ~ 11,其中0代表 1 月,10代表 11 月。如果不做+ 1处理,获取到的月份在整年里都会比实际少一个月。 -
获取当月号数(
now.getDate()):getDate()返回当前日期在所属月份中的天数(范围是1 ~ 31)。注意辨析同名的getDay(),后者返回的是星期几(星期日为 0,星期一至星期六为 1~6)。 -
标准化两位对齐补零(
padStart(2, '0')):ES2017 正式引入的String.prototype.padStart()方法,能在当前字符串长度未达到目标值时,在左侧重复填充指定字符。例如数值5转为字符串"5"后长度为 1,执行padStart(2, '0')后变为"05";而11长度已是 2,保持原样。这比早期广泛使用的('0' + m).slice(-2)更具语义化,避免隐式类型转换。
二、深度辨析:为什么常见单行简写容易导致“日期倒流”?
在很多技术博客和交流问答中,常能看到以下这种被奉为“一行搞定”的极简写法:
// ⚠️ 表面看起来极为精炼的常见写法
const naiveDate = new Date().toISOString().slice(0, 10);
这种写法为什么看似合理?因为它直接利用了 toISOString() 产生形如 "2026-11-10T08:30:00.000Z" 的 ISO 8601 标准字符串,再通过 slice(0, 10) 或 split('T')[0] 截取前面的年月日部分。在很多开发者白天的单元测试中,它确实输出了正确的当前日期。
toISOString() 输出的永远是 UTC 零时区 的标准时间(末尾带有 Z 标识),而不是用户所在环境的本地时区时间。对于处于东八区(UTC+8,例如中国标准时间)的系统,当地时间早晨 00:00:00 ~ 07:59:59 时,零时区时间实际上还停留在前一天的 16:00:00 ~ 23:59:59。
假设当前是北京时间 2026 年 5 月 2 日 早晨 06:30:00:
- 本地时间(Local Time):2026-05-02 06:30:00。本地日期是 5 月 2 日。
- 零时区时间(UTC):2026-05-01 22:30:00Z。零时区日期仍是 5 月 1 日。
- 此时调用
new Date().toISOString(),得到的是"2026-05-01T22:30:00.000Z"。 - 执行
slice(0, 10)后,截取出来的结果是"2026-05-01",比实际本地日期整整倒流了一天!
有些开发者为了保留 toISOString() 的简短,会写成:
new Date(Date.now() - new Date().getTimezoneOffset() * 60000).toISOString().slice(0, 10)。
这种做法虽然通过手动补偿毫秒差暂时绕过了时区差距,但它本质上伪造了一个时间戳来迎合格式化,严重违背了 Date 语义。在夏令时切换边界、跨时区服务器节点或多重转换中极其脆弱,代码晦涩难懂,远不如直接使用 getFullYear() 等原生方法清晰稳妥。
三、现代进阶方案:利用 Intl.DateTimeFormat
现代浏览器的 ECMAScript 国际化 API(Intl)为多语言和本地化格式处理提供了强大的底层支撑。如果你希望采用声明式的现代标准 API,同时避免手写拼装,Intl.DateTimeFormat 提供了两种非常优秀的方案。
方法 1:利用加拿大英语区域标签 ('en-CA')
在国际标准化区域格式中,加拿大区域设置(en-CA)的默认公历短日期格式恰好就是 ISO 8601 标准的 YYYY-MM-DD:
/**
* 基于 Intl 区域规范输出 YYYY-MM-DD
*/
function getLocalDateByIntl(date = new Date()) {
return new Intl.DateTimeFormat('en-CA', {
year: 'numeric',
month: '2-digit',
day: '2-digit'
}).format(date);
}
console.log(getLocalDateByIntl());
该方法完全在宿主环境的本地时区中执行计算,天然具备两位补零,且代码简洁优雅,兼容各大现代浏览器与 Node.js 14+ 环境。
方法 2:利用 formatToParts 进行结构化装配
如果你不希望在代码中硬编码任何特定国家代码,或者需要 100% 确定性的连接符号(保证在所有环境都是破折号),可以使用 formatToParts() 方法:
/**
* 结构化解析组件并安全拼接
*/
function formatDatePartsDeterministic(date = new Date()) {
const formatter = new Intl.DateTimeFormat('default', {
year: 'numeric',
month: '2-digit',
day: '2-digit'
});
const parts = formatter.formatToParts(date);
// 从拆分部件中精准提取对应字段
const year = parts.find(p => p.type === 'year').value;
const month = parts.find(p => p.type === 'month').value;
const day = parts.find(p => p.type === 'day').value;
return `${year}-${month}-${day}`;
}
console.log(formatDatePartsDeterministic());
四、工程拓展:第三方轻量库与未来规范
如果你的项目不仅需要获取当天的 YYYY-MM-DD,还需要频繁进行诸如“获取昨天”、“上周同一天”、“计算两个日期间隔天数”等复杂运算,建议使用现代轻量级不可变日期库:
1. Day.js(体积轻量,语法贴近 Moment)
Day.js 大小仅约 2KB,保留了类似于 Moment.js 的链式语法,是现代前端工程中最常用的轻量级选择:
import dayjs from 'dayjs';
// 获取当前本地日期并格式化
const today = dayjs().format('YYYY-MM-DD');
console.log(today); // "2026-11-10"
2. date-fns(函数式按需引入)
采用纯函数与模块化设计,完美支持 Tree-shaking:
import { format } from 'date-fns';
// 注意 date-fns 中年份小写 yyyy,月份大写 MM,日小写 dd
const today = format(new Date(), 'yyyy-MM-dd');
console.log(today); // "2026-11-10"
3. 未来标准:ECMAScript Temporal API
JavaScript 诞生数十年来,原生的 Date 对象因可变性、时区混乱、月份基于 0 索引等设计缺陷饱受诟病。TC39 正在推进的新一代现代日期时间规范 Temporal API 已进入 Stage 3/4。
在未来完全普及后,仅需一行语义极度精确的原生代码即可获取纯日期:
// 阶段性规范预览(原生支持后无需手动补零与计算)
const today = Temporal.Now.plainDateISO().toString();
console.log(today); // "2026-11-10"
五、核心 API 速查表与避坑自查清单
为了便于快速对照和在代码审查(Code Review)中排查问题,下表梳理了格式化日期时最核心的原生方法与常见陷阱:
| API 方法 | 预期作用 | 易错陷阱与表现 | 正确编写范式 |
|---|---|---|---|
getFullYear() |
获取 4 位完整公历年份 | 手误写为 getYear(),得到距 1900 年的偏移数值 |
date.getFullYear() |
getMonth() |
获取当前月份索引 | 忘记 + 1,导致获取结果比真实月份小 1(0~11) |
date.getMonth() + 1 |
getDate() |
获取当前月中天数(1~31) | 误写为 getDay(),取出了星期几(0~6) |
date.getDate() |
padStart(2, '0') |
为个位数字前导补零 | 直接对 Number 类型调用报错,未先转为 String | String(val).padStart(2, '0') |
toISOString() |
输出 ISO 8601 字符串 | 直接截取前10位,导致在东部时区清晨倒流回前一天 | 优先使用本地年月日拼接或 Intl |
六、总结与实践建议
实现“获取当前日期并格式化为 YYYY-MM-DD”看似是一个简单操作,但背后涉及到了 JavaScript Date 对象的时区基准、月份从零索引的历史设计、以及补零对齐的规范化处理。
- 常规推荐:优先采用封装好的
getFullYear() + getMonth() + 1 + getDate()搭配padStart(2, '0')。逻辑纯粹、零依赖、性能极高,适配所有现代浏览器和 Node.js 环境。 - 国际化场景:如果项目偏好现代声明式 API,采用
new Intl.DateTimeFormat('en-CA', ...).format()既语义优雅又天然符合 ISO 标准。 - 严加警惕:杜绝在未经时区确认的情况下使用
toISOString().slice(0, 10),避免清晨时段因 UTC 转换而引发整整一天的偏差问题。
实用工具推荐
绿化、压缩、噪点和虚拟水印,自由调节做旧程度。
选择画质和输出格式,预览压缩前后的大小。