datetime.daysinmonth 直接获取当月天数,参数为年份和1~12的月份,线程安全无副作用;闰年判断应使用 datetime.isleapyear 而非手动逻辑;二月天数必须用 daysinmonth(year, 2) 动态计算。

用 DateTime.DaysInMonth 直接拿当月天数
别自己算年份、月份再查表或写 if 判断——DateTime.DaysInMonth 就是干这个的,参数明确、无副作用、线程安全。
常见错误是手动拼 new DateTime(year, month, 1) 再加减,既绕又容易在跨年/跨月时出错;还有人用 DateTime.Now.AddDays(-1) 取上月末,结果遇到 31 号就崩(比如 3 月 31 日跑代码,2 月没 31 号)。
正确做法:
- 第一个参数填年份,第二个填月份(注意:月份是 1~12,不是 0~11)
- 想取当前月?直接用 DateTime.Now.Year 和 DateTime.Now.Month
- 示例:DateTime.DaysInMonth(DateTime.Now.Year, DateTime.Now.Month) → 返回 31、28、29 等整数
闰年判断别手写逻辑,用 DateTime.IsLeapYear
“能被 4 整除但不能被 100 整除,除非还能被 400 整除”这种规则看着简单,一写就漏边界——比如 1900 年不是闰年,2000 年才是。自己 if 嵌套容易写错,也难维护。
系统 API 已经封装好全部规则:
- DateTime.IsLeapYear(2000) → true
- DateTime.IsLeapYear(1900) → false
- DateTime.IsLeapYear(DateTime.Now.Year) → 当前年是否闰年
注意:它只认年份,不关心月份和日期;传入非法年份(如 0 或负数)会抛 ArgumentOutOfRangeException
二月天数必须结合闰年判断,不能硬编码
有人图省事,在代码里写死 if (month == 2) return 28;,上线后遇到 2024 年就少算一天。更隐蔽的是把闰年判断和 DaysInMonth 拆开两处用,结果年份来源不一致(比如一处用 DateTime.Today,另一处用 DateTime.Now,毫秒级差异在跨日时导致逻辑错乱)。
稳妥做法:
- 二月天数永远走 DateTime.DaysInMonth(year, 2),它内部已自动调用闰年逻辑
- 如果你只是想快速判断“今年二月有没有 29 号”,用 DateTime.IsLeapYear(DateTime.Now.Year) 更轻量
- 不要自己组合 year % 4 == 0 && ...,除非你在写教学 demo
时区和 DateTimeKind 对结果没影响
DaysInMonth 和 IsLeapYear 都只依赖年份和月份数值,完全不看时间部分、也不管 DateTimeKind 是 Unspecified、Local 还是 Utc。所以别担心时区转换会改变二月天数——它不会。
但要注意:如果你从用户输入、数据库或 API 拿到一个带时区的 DateTimeOffset,得先取它的 .Date.Year 和 .Date.Month 再传进去,别直接用 .DateTime(可能因本地化产生意外偏移)。
真正容易被忽略的点是:这些方法返回的是**日历意义上的天数**,不是工作日、也不是考虑节假日的业务日。如果需求是“本月有多少个工作日”,那就得另起炉灶了。










