半程生日计算器

半程生日是生日往后推六个日历月,目标月没有这一天就钳位到该月最后一天。1995 年 8 月 31 日推六个月是 1996 年 2 月 31 日,这一天不存在,于是取 1996 年 2 月的最后一天,也就是闰年的 2 月 29 日。填一个生日立刻出日期、间隔天数与星期。

默认口径:加 6 个日历月,目标月天数不足时钳位到该月最后一天。
生日 1995-08-31 | 加 6 个日历月 半程生日 = 1996-02-29(月末钳位:目标月只有 29 天) 间隔 = 182 天 该日是 星期四 按 182 天口径 = 1996-02-29 按 182.5 天口径(取整 183 天)= 1996-03-01 两种口径最大偏差 = 1 天 当前对比口径(182 天)= 1996-02-29 -- 加 1 ~ 12 个日历月 -- 1 个月 -> 1995-09-30(钳位) | 间隔 30 天 2 个月 -> 1995-10-31 | 间隔 61 天 3 个月 -> 1995-11-30(钳位) | 间隔 91 天 4 个月 -> 1995-12-31 | 间隔 122 天 5 个月 -> 1996-01-31 | 间隔 153 天 6 个月 -> 1996-02-29(钳位) | 间隔 182 天 7 个月 -> 1996-03-31 | 间隔 213 天 8 个月 -> 1996-04-30(钳位) | 间隔 243 天 9 个月 -> 1996-05-31 | 间隔 274 天 10 个月 -> 1996-06-30(钳位) | 间隔 304 天 11 个月 -> 1996-07-31 | 间隔 335 天 12 个月 -> 1996-08-31 | 间隔 366 天 -- 批量(加 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 天

默认示例:1995-08-31。日期、月数、对比口径任意一项改动即重新计算,光标停在日期框里按回车同样触发一次。

使用方法

  1. 「生日」默认 1995-08-31,允许 1900-01-01 到 2100-12-31。改日期、月数或对比口径任意一项立刻重算,光标停在日期框里按回车同样触发一次。
  2. 「加几个月」默认 6,范围 1 到 12。6 就是半程生日;改成 3 是一年里的四分之一点,改成 12 就是下一年的生日当天。
  3. 「对比口径」默认 182 天,另一个选项是 182.5 天(取整 183 天)。这一栏只影响对比行,不改变半程生日的计算结果。
  4. 「批量生日」默认已经写好五行示例,每行一个 YYYY-MM-DD 格式的日期。按 Ctrl+Enter(Mac 上 Cmd+Enter)一次算完;万一被清空,点计算时会自动填回。
  5. 结果区给出「生日 的半程生日 = 日期(间隔多少天,是否发生月末钳位)」这一行,下面跟着该日是星期几,以及两种固定天数口径的对比。
  6. 清单里有一张加 1 到 12 个日历月的逐月对照,能看清哪几个月会发生钳位;1995-08-31 在 1、3、6、8、10 个月上都会钳位。
  7. 清单下面是一张 12 行的逐月明细表,默认显示 6 行,点「加载更多」展开全部,点「导出 CSV」存成带字节序标记的表格文件。
  8. 日期为空、不是合法日历日(比如 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-311990-07-31181否
1992-03-311992-09-30183是
1995-05-311995-11-30183是
1995-08-311996-02-29182是
1998-10-311999-04-30181是
2000-12-312001-06-30181是
2000-02-292000-08-29182否
2001-07-152002-01-15184否
2004-02-292004-08-29182否
2010-06-302010-12-30183否
2015-11-302016-05-30182否
2020-04-302020-10-30183否

「半年」的两种算法放在一起看,加六个月得 181 到 184 天,与 182.5 天这个平均值最多差 2 天。

生日加 6 个月按 182 天按 182.5 天最大偏差
2000-03-15184 天182 天183 天1 天
2000-09-15181 天182 天183 天2 天
2001-07-01184 天182 天183 天1 天
2001-01-01181 天182 天183 天2 天
2000-02-01182 天182 天183 天1 天
2000-08-01184 天182 天183 天1 天
1999-11-30182 天182 天183 天1 天
2001-05-31183 天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 日及以内的生日永远不会溢出。

结果边界、舍入与适用说明

常见问题

半程生日是按 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 这一种写法,年月日用短横线分隔,月和日不足两位要补零。写成斜杠、点号或带中文的形式都识别不了,会直接报行号与内容,不做猜测。

这个工具免费吗?

免费,无需注册即可使用。

输入的数据安全吗?

安全。计算在当前浏览器本地完成,不会上传输入内容。

相关工具