.NET 原生 ChineseLunisolarCalendar 因设计陈旧、年份对齐错误、缺失节气干支生肖等字段且已过时,无法满足完整农历需求;推荐使用第三方 ChnCal 库,支持 1900–2100 年全量农历信息。

没有现成的“中国农历类”,.NET 原生只提供 ChineseLunisolarCalendar,但它不支持公历转农历的完整日期映射(比如节气、干支、生肖、闰月标识等),也不能直接获取“农历正月初一对应哪天公历”这类逆向查询。
为什么 ChineseLunisolarCalendar 不能直接用
这个类是 .NET Framework 1.0 就带的老古董,设计目标仅限于基础农历计算(如农历年月日转换),但存在几个硬伤:
- 它把农历年份强行对齐到公历年份(例如 2024 年 1 月 1 日算作农历 2024 年,实际农历 2024 年从 2024 年 2 月 10 日才开始)
- 不提供节气、中气、闰月标记、干支纪年 / 纪月 / 纪日、生肖等关键字段
- 在 .NET Core / .NET 5+ 中已被标记为
Obsolete,且行为与旧版不一致(比如某些日期会抛ArgumentOutOfRangeException) - 无法反查“某农历日期对应哪些公历日期”(如“2024 年闰二月十五”)
推荐方案:用第三方库 ChnCal(NuGet 包名)
这是目前 C# 生态里最轻量、准确、持续维护的农历库,基于紫金山天文台算法,覆盖 1900–2100 年,支持节气、干支、生肖、闰月、农历节日等全量字段。
- 安装:
dotnet add package ChnCal(或通过 NuGet 管理器搜索ChnCal) - 核心用法:
ChineseDate.FromDateTime(……)获取指定公历日期对应的农历信息 - 注意它返回的是不可变结构体
ChineseDate,所有字段(Year、Month、Day、IsLeapMonth、StemBranchYear、Zodiac、SolarTerm等)都可直接读取 - 不要用
ToString()直接输出——它默认格式是“农历 YYYY 年 MM 月 DD 日”,不带闰字、不区分大小写,建议手动拼接
var dt = new DateTime(2024, 2, 10); // 农历甲辰年正月初一 var cd = ChineseDate.FromDateTime(dt); Console.WriteLine($"{cd.Year}年 {(cd.IsLeapMonth ? " 闰 " : "")}{cd.Month} 月{cd.Day}日 "); // 输出:2024 年正月初一 Console.WriteLine($" 生肖:{cd.Zodiac},干支年:{cd.StemBranchYear}"); // 输出:生肖:龙,干支年:甲辰
如果必须用原生类,怎么绕过坑
仅限简单场景(如粗略判断“是否在农历某月内”),且接受误差(±1 个月):
- 先用
new ChineseLunisolarCalendar()实例化,再调用GetYear()/GetMonth()/GetDayOfMonth() - 必须传入
DateTimeKind.Unspecified或Local的DateTime,传Utc会出错 - 不能用于跨农历年边界日期(如 2024-01-28,实际属农历壬寅年腊月十九,但它会返回 2024 年一月)
- 获取闰月需额外判断:
calendar.GetMonthsInYear(year, ChineseEra.ChineseEra)返回 13 才表示该农历年有闰月
真正要显示“农历正月初一”“冬至”“端午节”这种业务需求,别省那几行代码——直接上 ChnCal。原生类的兼容性断层和语义偏差,后期排查比换库成本高得多。