使用方法
- 「生日」默认 1995-08-31,允许 1900-01-01 到 2100-12-31。改日期、月数或对比口径任意一项立刻重算,光标停在日期框里按回车同样触发一次。
- 「加几个月」默认 6,范围 1 到 12。6 就是半程生日;改成 3 是一年里的四分之一点,改成 12 就是下一年的生日当天。
- 「对比口径」默认 182 天,另一个选项是 182.5 天(取整 183 天)。这一栏只影响对比行,不改变半程生日的计算结果。
- 「批量生日」默认已经写好五行示例,每行一个 YYYY-MM-DD 格式的日期。按 Ctrl+Enter(Mac 上 Cmd+Enter)一次算完;万一被清空,点计算时会自动填回。
- 结果区给出「生日 的半程生日 = 日期(间隔多少天,是否发生月末钳位)」这一行,下面跟着该日是星期几,以及两种固定天数口径的对比。
- 清单里有一张加 1 到 12 个日历月的逐月对照,能看清哪几个月会发生钳位;1995-08-31 在 1、3、6、8、10 个月上都会钳位。
- 清单下面是一张 12 行的逐月明细表,默认显示 6 行,点「加载更多」展开全部,点「导出 CSV」存成带字节序标记的表格文件。
- 日期为空、不是合法日历日(比如 2 月 30 日)、超出年份范围,或批量行写成「1995/8/31」这类识别不了的形式,都会给出针对该条的具体提示。
这个半程生日计算器能解决什么问题
第一类是「半年」到底指哪一天。加六个日历月与加 182 天不是同一件事:前者月份固定偏移、天数可能变;后者天数固定、月份会漂移。1995-08-31 按加六个月得 1996-02-29,按加 182 天也得同一天,但换成 2000-09-15,加六个月是 2001-03-15,按 182 天却落在 2001-03-16。
第二类是月末生日必然遇到的问题。1 月、3 月、5 月、8 月、10 月、12 月的 31 日往后推六个月,目标月分别是 7 月、9 月、11 月、2 月、4 月、6 月,其中 9 月、11 月、2 月、4 月、6 月都没有 31 日。1998-10-31 推到 1999-04-30,2000-12-31 推到 2001-06-30,都是被钳位的结果。
第三类是闰年带来的额外一天。1995-08-31 的半程生日落在 1996 年 2 月,1996 年是闰年所以能取到 29 日;同样一个 8 月 31 日的生日,1997-08-31 的半程生日就是 1998-02-28,因为 1998 年不是闰年。同一天生日在不同年份会得到不同的半程日。
第四类是要一个能写进日历的具体日期。半程生日常用来做半年回顾、半年庆或半岁照,需要一个确定到星期几的日子。本页把星期一并给出,1996-02-29 是星期四,不用再翻日历。
这个工具在本站也叫「半程生日计算器」,名字里的「半程」指一年走到一半的位置,下面会讲清它为什么没有唯一定义。
计算原理与变量说明
规则只有两条。第一条是月份加法:把月数加到生日的月份上,超出 12 就进位到年份,公式是 目标年 = 年 + floor((月 + n − 1) / 12),目标月 = ((月 + n − 1) mod 12) + 1。加 6 个月时,2000-09-15 得到 2000-15-15 这个中间结果,进位后就是 2001-03-15。
第二条是月末钳位:目标月往往没有生日那一天,这时取该月的最后一天。实现上用「下个月第 0 天」这个写法取当月天数,即 new Date(年, 月, 0).getDate(),然后取 min(生日日, 当月天数)。1996 年 2 月有 29 天,所以 8 月 31 日钳位到 29 日;1998 年 2 月只有 28 天,同样的生日就钳位到 28 日。
间隔天数用两个日期的毫秒差除以 86400000 再四舍五入。之所以要四舍五入,是因为夏令时与闰秒会让某些时区的毫秒差不是整天数的整数倍;本页全部日期按当地时区的零点构造,不会出现半天这种结果。
「半年」没有唯一定义。ISO 8601 的持续期写法里,P6M 是六个日历月,P182D 是 182 天,两者是两个不同的持续期,不能互换。加六个月得到的间隔天数在 181 到 184 天之间浮动,取决于这六个月里各有多少天;按 182.5 天这个平均值算,最大偏差是 2 天。
钳位还是顺延,是两种约定。8 月 31 日加六个月得 2 月 31 日,这个日期不存在。钳位约定取 2 月的最后一天,也就是往前收;顺延约定则把它当成 3 月 3 日往后推。本页用钳位,与主流日历应用和多数日期库的做法一致,写进日历时不会跳到下一个月。
哪些生日会溢出。31 日的生日在 3 月、5 月、8 月、10 月、12 月会溢出,分别落到 9 月 30 日、11 月 30 日、2 月 28 日或 29 日、4 月 30 日、6 月 30 日;30 日与 29 日的生日只有 8 月会溢出;28 日及以内的生日无论哪个月都不会溢出,因为每个月至少有 28 天。
2 月 29 日生日不溢出。闰年的 2 月 29 日加六个月是 8 月 29 日,8 月有 31 天,装得下。真正麻烦的是出生在 2 月 29 日的人怎么过「每年」的生日,那是另一个问题,本页按公历日期照算。
完整示例(数值仅作演示)
默认示例是 1995-08-31 加 6 个日历月:目标月是 1996 年 2 月,该月有 29 天,29 小于 31,所以钳位到 1996-02-29,间隔 182 天,这一天是星期四。按 182 天口径算也是 1996-02-29,两种口径在这一天上完全重合。
把生日改成 2000-03-15:加 6 个月得 2000-09-15,间隔 184 天,没有发生钳位。改成 2000-09-15:加 6 个月得 2001-03-15,间隔 181 天,注意这里跨了年,月份 9 加 6 得 15,进位后是次年 3 月。
把生日改成 1997-08-31:目标月是 1998 年 2 月,只有 28 天,所以半程生日是 1998-02-28,间隔 181 天。与 1995-08-31 那组对比,同样是 8 月 31 日,一个落在 29 日一个落在 28 日,差别全在闰年。
批量框默认五行,都按加 6 个日历月算:1995-08-31 得 1996-02-29(钳位,182 天);2000-03-15 得 2000-09-15(184 天);2000-09-15 得 2001-03-15(181 天);1997-08-31 得 1998-02-28(钳位,181 天);2000-02-29 得 2000-08-29(182 天,不钳位)。
半程生日对照表
下表把十二个生日各加 6 个日历月,标出是否发生月末钳位以及间隔天数。
| 生日 | 半程生日 | 间隔天数 | 钳位 |
|---|---|---|---|
| 1990-01-31 | 1990-07-31 | 181 | 否 |
| 1992-03-31 | 1992-09-30 | 183 | 是 |
| 1995-05-31 | 1995-11-30 | 183 | 是 |
| 1995-08-31 | 1996-02-29 | 182 | 是 |
| 1998-10-31 | 1999-04-30 | 181 | 是 |
| 2000-12-31 | 2001-06-30 | 181 | 是 |
| 2000-02-29 | 2000-08-29 | 182 | 否 |
| 2001-07-15 | 2002-01-15 | 184 | 否 |
| 2004-02-29 | 2004-08-29 | 182 | 否 |
| 2010-06-30 | 2010-12-30 | 183 | 否 |
| 2015-11-30 | 2016-05-30 | 182 | 否 |
| 2020-04-30 | 2020-10-30 | 183 | 否 |
「半年」的两种算法放在一起看,加六个月得 181 到 184 天,与 182.5 天这个平均值最多差 2 天。
| 生日 | 加 6 个月 | 按 182 天 | 按 182.5 天 | 最大偏差 |
|---|---|---|---|---|
| 2000-03-15 | 184 天 | 182 天 | 183 天 | 1 天 |
| 2000-09-15 | 181 天 | 182 天 | 183 天 | 2 天 |
| 2001-07-01 | 184 天 | 182 天 | 183 天 | 1 天 |
| 2001-01-01 | 181 天 | 182 天 | 183 天 | 2 天 |
| 2000-02-01 | 182 天 | 182 天 | 183 天 | 1 天 |
| 2000-08-01 | 184 天 | 182 天 | 183 天 | 1 天 |
| 1999-11-30 | 182 天 | 182 天 | 183 天 | 1 天 |
| 2001-05-31 | 183 天 | 182 天 | 183 天 | 0 天 |
逐月看哪些生日会溢出,31 日的生日在半年里会遇到五次钳位。
| 生日 | 加 6 个月之后 | 是否钳位 |
|---|---|---|
| 1 月 31 日 | 7 月 31 日 | 否 |
| 3 月 31 日 | 9 月 30 日 | 是 |
| 5 月 31 日 | 11 月 30 日 | 是 |
| 7 月 31 日 | 次年 1 月 31 日 | 否 |
| 8 月 31 日 | 2 月 28 日或 29 日 | 是 |
| 10 月 31 日 | 4 月 30 日 | 是 |
| 12 月 31 日 | 6 月 30 日 | 是 |
| 30 日 / 29 日 / 28 日及以内 | 仅 8 月 30 日与 8 月 29 日会钳位 | 视月份 |
读第一张表看钳位:十二行里有六行发生了钳位,间隔天数从 181 到 184 不等。读第二张表看偏差:加六个月与 182.5 天这个平均值最多差 2 天,纪念日差一两天不影响使用,但对账时要说清用的是哪一种。读第三张表看边界:28 日及以内的生日永远不会溢出。
结果边界、舍入与适用说明
- 生日框为空时提示「生日还没有选,请填 YYYY-MM-DD 格式的日期」,不会拿上一次的结果冒充本次输出。
- 日期不是合法日历日时提示该月没有这一天,例如 2 月 30 日、4 月 31 日都会拦下并给出具体月份。
- 年份超出 1900 到 2100 时提示超出范围,本页不处理更早或更晚的日期。
- 「加几个月」填成 0、13 或小数时提示请填 1 到 12 之间的整数,不自动取整也不取默认。
- 批量行写成 1995/8/31、1995.08.31 或带中文的形式时,报出具体行号与内容,提示只接受 YYYY-MM-DD。
- 间隔天数按毫秒差除以 86400000 再四舍五入,不出现半天;跨夏令时的地区结果可能与当地挂历差一天。
- 本页按公历日期计算,不涉及农历与虚岁;2 月 29 日出生的人按公历日期照算。
- 页面不联网、不写本地存储,生日与批量内容只用于本次计算。
常见问题
半程生日是按 182 天算还是按加六个月算?
本页按加六个日历月算,这是主流日历应用的做法。按 182 天算会得到另一个日期:2000-09-15 加六个月是 2001-03-15,加 182 天却是 2001-03-16。两种口径最多差 2 天,本页把两种结果都列出来供你挑。
1995-08-31 的半程生日为什么是 2 月 29 日?
8 月加六个月是次年 2 月,2 月没有 31 日,于是钳位到 2 月的最后一天。1996 年是闰年,2 月有 29 天,所以取到 29 日。同样是 8 月 31 日,1997 年的生日推出来是 1998-02-28,因为 1998 年不是闰年。
钳位和顺延有什么区别?本页用哪一种?
2 月 31 日不存在,钳位取 2 月最后一天(往前收),顺延则当成 3 月 3 日(往后推)。本页用钳位,与主流日期库一致,写进日历时不会跳到下一个月。若你的场景要求顺延,需要自行把结果再加几天。
2 月 29 日出生的人半程生日怎么算?
闰年 2 月 29 日加六个月是 8 月 29 日,8 月有 31 天,装得下,不钳位。本页按公历日期照算,不涉及平年该在哪天过生日的约定问题。
加六个月得到的天数为什么在 181 到 184 之间?
因为这六个月里各月的天数不一样。跨过 2 月就少一天,跨过 7、8 月就多一天,于是间隔在 181 到 184 天之间浮动,而不是固定的 182 或 182.5 天。与 182.5 天这个平均值相比,最大偏差是 2 天。
批量框里写「1995/8/31」为什么报错?
只接受 YYYY-MM-DD 这一种写法,年月日用短横线分隔,月和日不足两位要补零。写成斜杠、点号或带中文的形式都识别不了,会直接报行号与内容,不做猜测。
这个工具免费吗?
免费,无需注册即可使用。
输入的数据安全吗?
安全。计算在当前浏览器本地完成,不会上传输入内容。