简介

简介#

[1]:
#r "nuget:YiJingFramework.Nongli"
using YiJingFramework.Nongli.Extensions;
using YiJingFramework.Nongli.Lunar;
using YiJingFramework.Nongli.Solar;
using YiJingFramework.PrimitiveTypes;
Installed Packages
  • YiJingFramework.Nongli, 4.1.0

此包提供了对中国农历的支持。

此包是通过打表的方式获取的节气等信息,而非直接从天文原理出发进行的计算。因此,这里首先要感谢 lunar-csharp 对相关社区作出的巨大贡献。本包所有用到的节气表等都是通过该包进行生成的,本包的测试项目也是依赖于该包的。

相比而言, lunar-csharp 非常强调“无依赖”,其功能非常丰富,但都汇集在同一个包中,且与日期本身关联性很强。以其提供的冲煞功能为例,可以获取某日时的日冲、时冲等,但不方便直接获取某个地支的六冲。而本包作为 YiJingFramework 中的一部分,与其他包共用 TianganDizhi 等类型。于是此包可以专注于农历日期本身,而更多的功能将由其他的包另外提供。

同时, 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.YearLunarDateTime.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)));
白露
秋分