朋友那张记录表有两列日期:一列把上次那个日子往后挪一个自然月,一列把它加 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 个输入丢进经期计算器里,它推的是周期乘倍数的天,不是自然月。