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 天都算得乾淨,落點自己挑。