- 文档
- 教程
- 前端
【免费下载链接】zh.javascript.info
现代 JavaScript 教程(The Modern JavaScript Tutorial),以最新的 ECMAScript 规范为基准,通过简单但足够详细的内容,为你讲解从基础到高阶的 JavaScript 相关知识。
本篇文章基于《现代 JavaScript 教程》(zh.javascript.info)日期和时间章节下的配套练习题“某月的最后一天?”,完整讲解getLastDayOfMonth(year, month)的实现思路、底层原理与测试验证。读完本文,你将掌握new Date(year, month, date)构造器的参数规则、Date对象"自动校准"(Autocorrection)这一关键特性,并学会用"下个月的第 0 天"这一技巧一行代码求出任意月份(含闰年二月)的天数,同时通过仓库内的 Mocha 测试用例验证结果正确性。
任务背景:为什么"某月最后一天"是个值得研究的题目
在 5-last-day-of-month/task.md 中给出了这样一道练习题:
写一个函数
getLastDayOfMonth(year, month)返回month月的最后一天。有时候是 30,有时是 31,甚至在二月的时候会是 28/29。
参数约定如下:
year—— 四位数的年份,比如 2012;month—— 月份,从 0 到 11(与 JavaScript 的Date月份约定一致,0 代表一月)。
示例要求:getLastDayOfMonth(2012, 1) = 29(2012 年是闰年,二月的最后一天是 29 日)。
这道题的"难点"在于:不同月份的天数不同(30/31),而且二月还要受闰年规则影响(28 或 29)。如果手写分支判断,需要维护一张月份天数表,还要额外实现闰年判定逻辑(year % 4 === 0 && year % 100 !== 0 || year % 400 === 0),既繁琐又容易出错。而Date对象本身就内置了完整的日历算法,可以优雅地解决这个问题。
前置知识:new Date(year, month, date) 的构造规则
要理解解法,必须先回顾 日期和时间 中关于构造函数的关键约定:
new Date(year, month, date, hours, minutes, seconds, ms)使用当前时区中的给定组件创建日期,只有前两个参数是必须的:
year应该是四位数。为了兼容性,也接受 2 位数,并将其视为19xx(例如98与1998相同),但强烈建议始终使用 4 位数;month计数从0(一月)开始,到11(十二月)结束——这是 JavaScript 初学者最容易踩的坑;date是当月的具体某一天,如果缺失,则为默认值1;- 如果
hours/minutes/seconds/ms缺失,则均为默认值0。
例如new Date(2011, 0, 1)表示 2011 年 1 月 1 日 00:00:00。访问日期组件时,getMonth()同样返回从 0 到 11 的月份,getDate()返回当月的具体日期(从 1 到 31)。
核心原理:Date 的自动校准(Autocorrection)
解法能够一行完成,依赖的是Date对象一个非常方便的特性——自动校准:
我们可以设置超范围的数值,它会自动校准。超出范围的日期组件将会被自动分配。
仓库文档中给出了两个典型例子:
let date = new Date(2013, 0, 32); // 32 Jan 2013 ?!? alert(date); // ……是 1st Feb 2013!日期组件越界时会被自动进位。反过来,设置 0 甚至负值同样有效:
let date = new Date(2016, 0, 2); // 2016 年 1 月 2 日 date.setDate(1); // 设置为当月的第一天 alert( date ); date.setDate(0); // 天数最小可以设置为 1,所以这里设置的是上一月的最后一天 alert( date ); // 31 Dec 2015正是因为"第 0 天"会被自动校准为"上个月的最后一天",我们才能用构造器直接求出目标月份的天数,而完全不必关心这个月到底是 28、29、30 还是 31 天——闰年逻辑全部由引擎内置的日历算法处理。
标准解法:下个月的第 0 天
仓库中的官方解答位于 5-last-day-of-month/solution.md,核心思路是:用下个月(month + 1)创建日期,但将天数(day)传为 0:
function getLastDayOfMonth(year, month) { let date = new Date(year, month + 1, 0); return date.getDate(); } alert( getLastDayOfMonth(2012, 0) ); // 31 alert( getLastDayOfMonth(2012, 1) ); // 29 alert( getLastDayOfMonth(2013, 1) ); // 28逐行拆解:
new Date(year, month + 1, 0):先构造"下个月"的日期对象;- 天数传
0:通常日期从 1 开始,但从技术上讲我们可以传递任何数字,日期会自动进行调整。因此当我们传递0时,它的意思是"一个月的第一天的前一天",换句话说——"上个月的最后一天"; date.getDate():取出这一天是几号,即目标月份的最后一天。
对照任务示例验证:getLastDayOfMonth(2012, 1)中month = 1表示二月,new Date(2012, 2, 0)即"2012 年 3 月 0 日"被自动校准为 2012 年 2 月 29 日(2012 是闰年),getDate()返回 29,完全符合题目要求。
仓库中的独立实现文件 5-last-day-of-month/_js.view/solution.js 与上述解答完全一致,可直接复制运行:
function getLastDayOfMonth(year, month) { let date = new Date(year, month + 1, 0); return date.getDate(); }测试验证:仓库内置的 Mocha 用例
该练习目录下还带有配套的测试文件 5-last-day-of-month/_js.view/test.js,使用 Mocha +assert对三个典型场景做了断言:
describe("getLastDayOfMonth", function() { it("last day of 01.01.2012 - 31", function() { assert.equal(getLastDayOfMonth(2012, 0), 31); }); it("last day of 01.02.2012 - 29 (leap year)", function() { assert.equal(getLastDayOfMonth(2012, 1), 29); }); it("last day of 01.02.2013 - 28", function() { assert.equal(getLastDayOfMonth(2013, 1), 28); }); });三个用例分别覆盖了:
| 调用 | 含义 | 期望结果 | 覆盖场景 |
|---|---|---|---|
getLastDayOfMonth(2012, 0) | 2012 年 1 月 | 31 | 31 天的大月 |
getLastDayOfMonth(2012, 1) | 2012 年 2 月 | 29 | 闰年二月 |
getLastDayOfMonth(2013, 1) | 2013 年 2 月 | 28 | 平年二月 |
可以看到,测试刻意选择了"相邻年份的同一月份",以此证明解法对闰年判定(28/29)的处理是自动且正确的,无需任何手写的闰年分支。
延伸思考:这个技巧还能用在哪儿
"第 0 天 / 自动校准"并不是一个孤立的小技巧,它是Date对象最通用的能力之一,在本教程的其他练习中同样有体现:
- 计算 N 天前的日期:配套练习 4-get-date-ago/solution.md 中,通过
date.setDate(date.getDate() - days)处理跨月甚至跨年的减法,例如从 2015 年 1 月 2 日减去 2 天得到 2014 年 12 月 31 日、减去 365 天得到 2014 年 1 月 2 日,日期自动校准帮你处理了所有边界; - 构造指定日期:如 1-new-date/task.md 所示,用
new Date(2012, 1, 20, 3, 12)创建当地时区的 "Feb 20, 2012, 3:12am"; - 日期加减运算:
date.setDate(date.getDate() + 2)在 2016 年 2 月 28 日上加两天,自动得到 3 月 1 日(闰年)——日期和时间 文档中的"自动校准"小节对此有完整阐述; - 获取当月剩余天数、判断某月天数、生成日历组件等前端常见需求,都可以复用"下个月第 0 天"这一模式。
小结
| 要点 | 结论 |
|---|---|
| 构造器规则 | new Date(year, month, date)中month从 0 开始,date缺省为 1 |
| 自动校准 | 超出范围(含 0 和负值)的日期组件会被引擎自动进位/借位 |
| 核心技巧 | new Date(year, month + 1, 0)即"下个月的第 0 天"= 本月的最后一天 |
| 最终实现 | getLastDayOfMonth(year, month)仅需两行:构造 +getDate() |
| 正确性保证 | 仓库配套 test.js 验证了 31 天大月、闰年二月 29 日、平年二月 28 日三种情形 |
这道题完美诠释了 JavaScriptDate对象"内置日历算法 + 自动校准"的设计哲学:与其手写月份天数和闰年判断,不如把日历计算交给引擎,让代码更短、更不易出错。这一模式在日期处理的各类实战场景中都可以直接复用。
- 文档
- 教程
- 前端
【免费下载链接】zh.javascript.info
现代 JavaScript 教程(The Modern JavaScript Tutorial),以最新的 ECMAScript 规范为基准,通过简单但足够详细的内容,为你讲解从基础到高阶的 JavaScript 相关知识。
相关推荐
Modern JavaScript 教程实战:巧用 Date 自动校正,一行代码获取任意月份最后一天
Modern JavaScript 教程实战:巧用 Date 自动校正,一行代码获取任意月份最后一天 在 JavaScript 中判断"某个月有多少天"看似简单
文档/教程前端JavaScript 获取某月最后一天:利用 Date 自动校准的“0 日技巧”详解
JavaScript 获取某月最后一天:利用 Date 自动校准的“0 日技巧”详解 本指南围绕《现代 JavaScript 教程(The Modern Jav
文档教程前端用 JavaScript Date 自动校正计算月份最后一天:getLastDayOfMonth 实战详解
用 JavaScript Date 自动校正计算月份最后一天:getLastDayOfMonth 实战详解 在 JavaScript 中计算"某年某月的最后一天
文档/教程前端
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考