雜記 › 生活隨筆 › 每月同一天和每 28 天一次,一年差出 29 天

每月同一天和每 28 天一次,一年差出 29 天

朋友那張記錄表有兩列日期:一列把上次那個日子往後挪一個自然月,一列把它加 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-282026-02-280
第 3 次2026-04-302026-04-25-6
第 6 次2026-07-312026-07-18-13
第 12 次2027-01-312027-01-02-29
第 24 次2028-01-312027-12-04-58
起點 2026-01-31:每 28 天 減 每月同日 的差(天)0-6-13-29-58第 1 次第 3 次第 6 次第 12 次第 24 次

圖 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 格。

35 格視窗的實際填色,每格 1 天起 2024-02-29 經期 3 週期 21:經期 3、易孕期 9、安全期 9、下次經期 3、未到 11起 2026-03-08 經期 10 週期 45:經期 10、易孕期 9、安全期 16、下次經期 0下次月經在第 46 天,35 格畫不出來

圖 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 個輸入丟進經期計算器裡,它推的是週期乘倍數的天,不是自然月。

📎 相關文章

照片轉了 90°,換一部裝置方向又回來了:EXIF 標記與真實旋轉的分工 2026-10-22 用隨機比較器打亂名單,抽籤就已經不公平了 2026-10-21 加侖有兩種、馬力有兩種、位元組有兩套進位:換算前先問是哪一種 2026-10-21 2 月 29 日出生的人,平年哪天滿 18 歲?兩種週年口徑差一天 2026-10-21 全馬 3:30 要跑多快?九段遞進配速表、前後半差和那段觸底的配速 2026-10-21