朋友那張記錄表有兩列日期:一列把上次那個日子往後挪一個自然月,一列把它加 28 天。以 2026-01-31 為起點,2 月兩列都寫 2026-02-28;到年底一列是 2027-01-31、一列是 2027-01-02,差 29 天。兩個人都認定自己沒錯,就拿著表去對照頁面實作——每月同一天和每 28 天一次,本來就是兩套算術。
這一頁只做按天的事
period-calculator 的模板 244 行,腳本在 L87 到 L243。我把腳本原樣抽出來跑在 DOM 樁上(Node v24,凍結今天 2026-10-22):全文 0 處 setMonth,setDate 只有 1 處,就是 L125-129 的 addDays,函式體 d.setDate(d.getDate() + n)。這一頁裡沒有日曆月,預測全是起點加上週期的整數倍天。
「未來 3 次經期預測」因此是 1、2、3 倍週期,不是 3 個自然月:起 2026-01-31、週期 28,給的是 2026-02-28、2026-03-28、2026-04-25,第 3 次既不是 31 號也不是月末。文案那句「可預測未來 3 個月的經期日期」按日曆月該走到 2026-04-30,差 5 天。反方向也得防:JS 裸 setMonth 把 2026-01-31 推到 2026-03-03,2 月沒有 31 號就直接溢出;自己寫月末夾取才給 2026-02-28,而那段夾取在本頁並不存在,只是我另起的外部對照。
兩條軌道從第幾次分岔
日曆月長度在 28 到 31 天之間擺動,28 天一檔是定長,並排一列就露餡:
| 次數 | 每月同日 | 每 28 天 | 差 |
|---|---|---|---|
| 第 1 次 | 2026-02-28 | 2026-02-28 | 0 |
| 第 3 次 | 2026-04-30 | 2026-04-25 | -6 |
| 第 6 次 | 2026-07-31 | 2026-07-18 | -13 |
| 第 12 次 | 2027-01-31 | 2027-01-02 | -29 |
| 第 24 次 | 2028-01-31 | 2027-12-04 | -58 |
圖 1:本頁 28 天口徑比日曆月同日提前多少天,綠柱那格就是標題裡的 29 天;同日是頁面之外的夾取對照。
第 2 次已經差 -3 天,2026-03-31 對 2026-03-28。想湊「一年就算 12 個 30 天」兩頭都不對:同一起點加 360 天是 2027-01-26,加 372 天是 2027-02-07,12 個 28 天只走到 2027-01-02。跨閏年再多一層:2026-01-31 加 365 天正好是 2027-01-31,2024-01-31 要加 366 天才回得到 2025-01-31,只加 365 天停在 2025-01-30。2 月幾天這頁全交給 JS Date,模板 0 處自寫閏年判定:2024 年 29 天、2000 年 29 天、1900 年 28 天、2100 年 28 天。2 月 29 號出生怎麼算週年,閏日生日那篇裡爭執過。
輸出是定型的,缺的那塊真沒有
面板固定 4 個 badge——下次月經、排卵日、易孕期、安全期,外加 3 行預測和 5 行日期說明。它只有 1 個日期輸入,沒有結束日期,所以給不出兩個日子相差幾天,也沒有年/月/週/時/分各級分解:唯一的除法是 L122 的 Math.round((target - today) / 86400000),粒度只有天,帶符號的天數僅說明裡那行「距今天 -236 天」。排卵日恆為下次月經前 14 天,易孕期恆為前 5 天到後 4 天共 10 天,安全期卻有兩套口徑:badge 在 2026-02-19 到 2026-02-27 之間只給 9 天,時間軸同一組輸入判出 13 格綠色。免責寫得很直白,不能作為避孕依據。要盯某個日子還剩多久,得去節日倒數那類以目標日為輸入的頁。
時間軸只有 35 格,畫不下不等於沒到
var totalDays = 35 把視窗寫死了,7 列網格。起 2026-01-31、經期 5 的那組掃描裡,週期 21 時下次經期佔 5 格,週期 27 仍是 5 格,週期 33 只剩 2 格,週期 39 和 45 歸零——週期 45 的下次月經落在第 46 天,在視窗外,尾部整片被綠色補滿。起 2026-03-08、經期 10、週期 45 那組是經期 10 格、易孕期 9 格、安全期 16 格,下次經期與未到都是 0,末格 title 只寫到第 35 天:沒有紅格是截斷,不是沒到。判定還講先到先得,經期 10、週期 21 時那 10 天的易孕期被吃掉 8 天,只剩 2 格。
圖 2:同一塊 35 格在兩個週期下的填色,深為經期、淺灰為易孕期、綠為安全期、半透明為下次經期、空為未到。
輸入側幾乎不報錯
整頁只有 3 條提示:請選擇上次月經開始日期、經期天數應在 2-10 之間、月經週期應在 21-45 之間。日期框沒有 min 和 max,parseDate 只 split('-') 再 parseInt,不判合法性:2026-02-31 被吞成 2026-03-03,下次月經照樣給 2026-03-31;2026-13-01 吞成 2027-01-01;少一段的 2026-10 和頂到 JS Date 上限的 275761-01-01 連提示都沒有,badge 直接寫 NaN-NaN-NaN。小數在函式層被截斷,經期 5.9、週期 28.9 照樣出結果。上限是 21-45,文案那句「正常月經週期21-35天」攔不住任何東西,週期填 40 就給 2026-03-12。所以核對時先讀回顯的日期串,別等紅字。
被追問的三個問題
Q:記錄時到底按哪一個?
按週期天數,也就是本頁這套:起點加週期的整數倍。別在一張表裡一半按日曆同日、一半按定長天數——第 12 次差 29 天,第 24 次差 58 天,兩條軌道各自成立,越走越不相遇。
Q:它不會自動把 31 號挪到 2 月末嗎?
不會。全文 0 處 setMonth,也沒有夾取分支。2026-01-31 加 28 天是 2026-02-28,加 30 天是 2026-03-02,落在月末純屬週期長度恰好對上。
Q:安全期那一欄能當依據嗎?
不能,免責原文就是不能作為避孕依據;同一組輸入下 badge 的 9 天和時間軸的 13 格,連長度口徑都對不齊。
回到那張兩列表:2026-02-28 那一格兩種寫法給的是同一個日子,年底一個寫 2027-01-31、一個寫 2027-01-02,誰都沒算錯,是兩句話本來不是一件事。要長期記,先把口徑寫進表頭,再挑工具——工具只是替你沿選定那條推到底。把這 3 個輸入丟進經期計算器裡,它推的是週期乘倍數的天,不是自然月。