28 天还是 29 天,这一页不会替你选
我在 2026-01-31 想找一个「一个月后」的日期,习惯性地去摸填月份的格子——这页没有。四个输入框只认天、时、分、秒,「月」和「年」不是可加的单位,唯一的算法是第 169 行那句毫秒平移:(days * 86400 + hours * 3600 + mins * 60 + secs) * 1000。于是「1 月 31 日加一个月」变成一道换算题:填 28 天,落点 2026-02-28 星期六,时间戳 1772208000;填 29 天,落点 2026-03-01 星期日,时间戳 1772294400。两个答案都是这页给的,彼此差 86400 秒,正好一天的量。
四个框之外没有月份入口
翻一遍模板,getMonth 与 getFullYear 各只出现在第 104 行和第 140 行的格式化函数里,是读不是写。页面上也没有「计算」「清空」按钮,<button> 只有 2 个且都是模式切换;空框与非数字按 0 算,所以填 0 天是恒等(2026-01-31 00:00:00,时间戳 1769788800),填 31 天到 2026-03-03、平移 2678400 秒,跟「一个月」不是一回事。
图 1:同一个 1 月 31 日起点,柱高按平移秒数等比。
108 次扫描里,没有哪个天数能常年当一个月
我不甘心,就把 12 个月 × 日号 29/30/31 × 偏移 28/30/31 共 108 次调用扫了一遍。2026 年 36 个基准日里有 7 个在 JS 层不存在(2/29、2/30、2/31、4/31、6/31、9/31、11/31 会各自滚成 3/1、3/2、3/3、5/1、7/1、10/1、12/1),可行的 29 个里固定 28 天命中下月同日 0 次、30 天 8 次、31 天 14 次;闰年 2028 可行基准 30 个,28 天 0 次、30 天 8 次、31 天 15 次。
| 偏移 | 2026 年命中 | 2028 年命中 |
|---|---|---|
| 28 天 | 0 / 29 | 0 / 30 |
| 30 天 | 8 / 29 | 8 / 30 |
| 31 天 | 14 / 29 | 15 / 30 |
最好的固定天数也只对齐 15/30,12 个月没有一个通用答案。
减法与闰日也只认天数
- 2026-03-01 减 1 天 → 2026-02-28;2026-01-01 减 1 天 → 2025-12-31,时间戳 1767110400。
- 2026-03-31 减 30 天只回到 2026-03-01,减 31 天才到 2026-02-28。
- 1970-01-01 减 1 天 → 1969-12-31 00:00:00,时间戳 -115200。
- 2028-02-29 加 365 天 → 2029-02-28(31536000 秒),加 366 天 → 2029-03-01,加 1461 天 → 2032-02-29,加 2921 天 → 2036-02-28;这闰年到底该填 365 还是 366,页面从不提示。
非法日期不报错:17 个畸形串回读全是空串
报错同样指望不上。我在 Chrome 里试了 17 个畸形串,2026-02-30T00:00、2026-13-01T00:00、2026-01-32T00:00、2026-01-31T25:00、13/1/2026 都在内,按 value 回读全部变成空串,validity 的七个标志一起 false,页面走第 154–158 行的空值分支,屏幕上只是重新显示一个空盒子,一个报错都没有。函数层是另一套:new Date("2026-02-30T00:00") 先滚成 2026-03-02,再加 1 天,结果打印 2026-03-03 00:00:00,时间戳 1772467200。
第 164 行那句「起始时间无效」在真实浏览器里只有一条路:5 到 6 位年份——控件原样留着 10000-01-01T00:00 到 275760-09-13T00:00 共 8 个样本,new Date() 对 8 个全部判 Invalid,而能解析的年份字符串实测最大到 9999。控件把非法串抹成空值,和页面弹出错误,是两件不同的事。
结果不校验:99979547 天渲染成 NaN
第 170 行往后没有任何 isNaN 判断。基准 2026-01-01T00:00 时正向最大 99979546 天 → 275760-09-13 00:00:00;再多一天的 99979547 天被渲染成 NaN-NaN-NaN NaN:NaN:NaN,时间戳写 NaN,星期栏空白,结果区从 display:none 变成可见,距今那行写着 已过 NaN 天 NaN 小时。上限还跟着基准移动:基准换成 2026-06-15T10:30 后 99979380 天才有效,99979381 已经 NaN;负向 100020453 天才有效(-271821-04-21 00:05:43),前后相差 40907 天,因为 V8 的 Date 只覆盖 ±8640000000000000 毫秒。
时间差不含首日,而且一路 floor
两个日期的差(第 224–235 行)不把首日算进去,并且每一项都取整:2026-01-01 到 2026-01-31 给 30 天 / 720 小时 / 43200 分钟 / 2592000 秒,含首尾两种都数上是 31 天;2026 年 1/1 到 3/1 是 59 天,2028 年是 60 天。同一分钟内给全 0——B 填 2026-01-01T00:00:00.500,那 500 毫秒被第 227 行吃掉,四个格子全 0;反向差 1 秒打成 -0 天 0 时 0 分 1 秒,格子是 -0 天 / -0 小时 / -0 分钟 / -1 秒,公式行 时间差 = 0天 0时 0分 1秒 却不带符号。
常见问答
Q:「一个月」到底填 28 还是 29 天?
看口径:要截在月末就填 28 天(2026-02-28),要进到下月 1 日就填 29 天(2026-03-01),这页两个都给、也不标注该用哪个,两者差 86400 秒。换个月末,答案又漂到 30 天或 31 天,命中率前面已经列成表。
Q:填错日期为什么没提示?
畸形串在控件层就被抹成空值,走第 154–158 行的空分支,只重新显示空盒子,不算报错;那句「起始时间无效」只有 5 到 6 位年份这 8 个样本才碰得到。我的证据都来自 value 回读,键盘逐字符输入的路径没有单独取证。
Q:能一次排完 12 个月吗?
不行,这页没有月份与年份的输入单位,也没有批量列表,<table> 与 <select> 各 0 处。108 次扫描里最好的固定天数只有 15/30,要逐月对齐得自己按当月天数换算,再把结果填进时间加减计算页的天数框。
我的小抄就三行:小月 30 天、大月 31 天、2 月 28 或 29 天。换算这一步页面不肯替我做,但它把加 28 天与加 29 天都算得干净,落点自己挑。