简介#
[1]:
#r "nuget:YiJingFramework.Nongli"
using YiJingFramework.Nongli.Extensions;
using YiJingFramework.Nongli.Lunar;
using YiJingFramework.Nongli.Solar;
using YiJingFramework.PrimitiveTypes;
- YiJingFramework.Nongli, 4.1.0
此包提供了对中国农历的支持。
此包是通过打表的方式获取的节气等信息,而非直接从天文原理出发进行的计算。因此,这里首先要感谢 lunar-csharp 对相关社区作出的巨大贡献。本包所有用到的节气表等都是通过该包进行生成的,本包的测试项目也是依赖于该包的。
相比而言, lunar-csharp 非常强调“无依赖”,其功能非常丰富,但都汇集在同一个包中,且与日期本身关联性很强。以其提供的冲煞功能为例,可以获取某日时的日冲、时冲等,但不方便直接获取某个地支的六冲。而本包作为 YiJingFramework 中的一部分,与其他包共用 Tiangan
、 Dizhi
等类型。于是此包可以专注于农历日期本身,而更多的功能将由其他的包另外提供。
同时, lunar-csharp 支持的时间跨度非常大。除了向后的推演计算,更困难的是还支持向前的查询,因为古时的历法系统其实是相对混乱的,会出现各种例外情况。这些都需要进行相关的研究才能确定如何取舍,不能完全根据现在的天文情况进行推断。本包考虑到在非专业情况下,一般不会需要古时的日期,更不会把它在农历和公历之间进行转换,而且由于是通过打表进行实现的,因此,本包没有支持太长的时间跨度。在有需要的情况下,更新一下相关表格就即可进行扩展。
本包使用起来也比较简单,举例而言,要获取今日的农历日期:
[2]:
DateTime now = new DateTime(2023, 8, 26, 20, 44, 11);
LunarDateTime lunar = LunarDateTime.FromGregorian(now);
var n = $"{lunar.Nian:C}年";
var y = $"{(lunar.IsRunyue ? '闰' : '平')}{lunar.Yue}月";
var r = $"{lunar.Ri}日";
var s = $"{lunar.Shi:C}时";
Console.WriteLine($"{n} {y} {r} {s}");
s = $"{lunar.YearInChinese()}年{lunar.YueInChinese()}月{lunar.RiInChinese()}";
Console.WriteLine(s);
SolarDateTime solar = SolarDateTime.FromGregorian(now);
s = $"{solar.Nian:C} {solar.Yue:C} {solar.Ri:C} {solar.Shi:C}";
Console.WriteLine(s);
癸卯年 平7月 11日 戌时
二〇二三年七月十一
癸卯 庚申 丙辰 戊戌
此包区分了农历的阳历部分和阴历部分。其中,阳历部分即干支历,年月日时均以干支表示;阴历部分即几月初几之类,月与日用数字表示,但时辰和年仍使用干支。
上述代码其实是演示了如何从公历转为农历。同时,此包也提供了从农历转到公历的方式:
[3]:
LunarNian nian = LunarNian.FromGregorian(2023);
LunarYue yue = nian.Yues[0];
LunarDateTime lunar = yue.GetDateTime(ri: 15, shi: Dizhi.Chen);
Console.WriteLine(lunar);
Console.WriteLine(lunar.ToGregorian());
N:Guimao2023 Y:C01 R:15 S:Chen
2/5/2023 8:00:00 AM
其中我们使用了 LunarNian.FromGregorian(2023)
以创建一个 LunarNian
。这里传入的 2023
以表示要获取的农历年年首位于公历 2023 年内,因为只靠干支是无法判断具体是哪一年的,于是就没法知道此年内月份的情况,更没有办法进行公历日期的转换。也就是说,其实农历年年首所在的公历年是会被切实记录的,可以通过 LunarNian.Year
和 LunarDateTime.Year
获取。阳历部分中也有类似的情况。
同时,转换的结果是 2023/2/5 8:00:00
,这意味着它是使用时辰中间的时间作为小时数。同时,子时将会被认定为第二天,包括在 LunarDateTime.FromGregorian
中也是一样:
[4]:
var dateTime = DateTime.Parse("2023/8/26 23:12:22");
var lunar = LunarDateTime.FromGregorian(dateTime);
Console.WriteLine(lunar);
Console.WriteLine(lunar.ToGregorian());
N:Guimao2023 Y:C07 R:12 S:Zi
8/27/2023 12:00:00 AM
在阳历部分中也有相同的情况,同时,阳历部分还涉及到交节换月。与大多数情况不同,为了和阴历部分保持一致,我们选择在交节当天换月,而不是交节时刻。如有需要,可以通过 SolarYue.Jieling
自行判断是否还没有到交节时刻:
[5]:
var dateTime8822 = DateTime.Parse("2023/8/8 22:59:59");
var solar8822 = SolarDateTime.FromGregorian(dateTime8822);
Console.WriteLine(solar8822.Yue);
var dateTime8722 = DateTime.Parse("2023/8/7 22:59:59");
var solar8722 = SolarDateTime.FromGregorian(dateTime8722);
Console.WriteLine(solar8722.Yue);
var dateTime8723 = DateTime.Parse("2023/8/7 23:00:00");
var solar8723 = SolarDateTime.FromGregorian(dateTime8723);
Console.WriteLine(solar8723.Yue);
var jieling = solar8723.SolarYue.Jieling;
Console.WriteLine(dateTime8723 < jieling);
Console.WriteLine(dateTime8822 > jieling);
Gengshen
Jiwei
Gengshen
True
True
最后,关于节气,此包中,暂时还没有提供直接明确的支持,但也可以通过 SolarYue
间接获取:
[6]:
static string GetJieqi(DateTime dateTime)
{
var jieqiTable = new string[] {
"立春", "雨水", "惊蛰", "春分", "清明", "谷雨",
"立夏", "小满", "芒种", "夏至", "小暑", "大暑",
"立秋", "处暑", "白露", "秋分", "寒露", "霜降",
"立冬", "小雪", "大雪", "冬至", "小寒", "大寒"
};
var solar = SolarDateTime.FromGregorian(dateTime);
var jieqiIndex = solar.SolarYue.IndexInNian * 2;
if (!solar.IsBeforeYueZhongqi)
jieqiIndex++;
return jieqiTable[jieqiIndex];
}
Console.WriteLine(GetJieqi(new DateTime(2023, 9, 22)));
Console.WriteLine(GetJieqi(new DateTime(2023, 9, 23)));
白露
秋分