mirror of
https://github.com/wwiinnddyy/LanMountainDesktop.git
synced 2026-06-22 00:54:26 +08:00
228 lines
6.0 KiB
C#
228 lines
6.0 KiB
C#
using System;
|
|
using System.Globalization;
|
|
|
|
namespace LanMountainDesktop.Services;
|
|
|
|
public sealed class LunarCalendarService
|
|
{
|
|
private static readonly ChineseLunisolarCalendar Calendar = new();
|
|
|
|
private static readonly string[] HeavenlyStemsZh =
|
|
[
|
|
"\u7532",
|
|
"\u4e59",
|
|
"\u4e19",
|
|
"\u4e01",
|
|
"\u620a",
|
|
"\u5df1",
|
|
"\u5e9a",
|
|
"\u8f9b",
|
|
"\u58ec",
|
|
"\u7678"
|
|
];
|
|
|
|
private static readonly string[] EarthlyBranchesZh =
|
|
[
|
|
"\u5b50",
|
|
"\u4e11",
|
|
"\u5bc5",
|
|
"\u536f",
|
|
"\u8fb0",
|
|
"\u5df3",
|
|
"\u5348",
|
|
"\u672a",
|
|
"\u7533",
|
|
"\u9149",
|
|
"\u620c",
|
|
"\u4ea5"
|
|
];
|
|
|
|
private static readonly string[] HeavenlyStemsEn =
|
|
["Jia", "Yi", "Bing", "Ding", "Wu", "Ji", "Geng", "Xin", "Ren", "Gui"];
|
|
|
|
private static readonly string[] EarthlyBranchesEn =
|
|
["Zi", "Chou", "Yin", "Mao", "Chen", "Si", "Wu", "Wei", "Shen", "You", "Xu", "Hai"];
|
|
|
|
private static readonly string[] ZodiacsZh =
|
|
[
|
|
"\u9f20",
|
|
"\u725b",
|
|
"\u864e",
|
|
"\u5154",
|
|
"\u9f99",
|
|
"\u86c7",
|
|
"\u9a6c",
|
|
"\u7f8a",
|
|
"\u7334",
|
|
"\u9e21",
|
|
"\u72d7",
|
|
"\u732a"
|
|
];
|
|
|
|
private static readonly string[] ZodiacsEn =
|
|
["Rat", "Ox", "Tiger", "Rabbit", "Dragon", "Snake", "Horse", "Goat", "Monkey", "Rooster", "Dog", "Pig"];
|
|
|
|
private static readonly string[] LunarMonthsZh =
|
|
[
|
|
"\u6b63",
|
|
"\u4e8c",
|
|
"\u4e09",
|
|
"\u56db",
|
|
"\u4e94",
|
|
"\u516d",
|
|
"\u4e03",
|
|
"\u516b",
|
|
"\u4e5d",
|
|
"\u5341",
|
|
"\u51ac",
|
|
"\u814a"
|
|
];
|
|
|
|
private static readonly string[] LunarDayDigitsZh =
|
|
[
|
|
"\u4e00",
|
|
"\u4e8c",
|
|
"\u4e09",
|
|
"\u56db",
|
|
"\u4e94",
|
|
"\u516d",
|
|
"\u4e03",
|
|
"\u516b",
|
|
"\u4e5d",
|
|
"\u5341"
|
|
];
|
|
|
|
public LunarCalendarInfo GetLunarInfo(DateTime dateTime)
|
|
{
|
|
var date = dateTime.Date;
|
|
try
|
|
{
|
|
var lunarYear = Calendar.GetYear(date);
|
|
var rawLunarMonth = Calendar.GetMonth(date);
|
|
var lunarDay = Calendar.GetDayOfMonth(date);
|
|
|
|
var (lunarMonth, isLeapMonth) = NormalizeLunarMonth(lunarYear, rawLunarMonth);
|
|
|
|
var sexagenaryYear = Calendar.GetSexagenaryYear(date);
|
|
var stemIndex = Calendar.GetCelestialStem(sexagenaryYear) - 1;
|
|
var branchIndex = Calendar.GetTerrestrialBranch(sexagenaryYear) - 1;
|
|
|
|
var ganzhiYearZh = $"{HeavenlyStemsZh[stemIndex]}{EarthlyBranchesZh[branchIndex]}";
|
|
var ganzhiYearEn = $"{HeavenlyStemsEn[stemIndex]}-{EarthlyBranchesEn[branchIndex]}";
|
|
var zodiacZh = ZodiacsZh[branchIndex];
|
|
var zodiacEn = ZodiacsEn[branchIndex];
|
|
|
|
return new LunarCalendarInfo(
|
|
LunarYear: lunarYear,
|
|
LunarMonth: lunarMonth,
|
|
LunarDay: lunarDay,
|
|
IsLeapMonth: isLeapMonth,
|
|
LunarDateZh: BuildLunarDateZh(lunarMonth, lunarDay, isLeapMonth),
|
|
LunarDateEn: BuildLunarDateEn(lunarMonth, lunarDay, isLeapMonth),
|
|
GanzhiYearZh: ganzhiYearZh,
|
|
GanzhiYearEn: ganzhiYearEn,
|
|
ZodiacZh: zodiacZh,
|
|
ZodiacEn: zodiacEn);
|
|
}
|
|
catch (ArgumentOutOfRangeException)
|
|
{
|
|
// ChineseLunisolarCalendar has a limited date range.
|
|
return new LunarCalendarInfo(
|
|
LunarYear: date.Year,
|
|
LunarMonth: date.Month,
|
|
LunarDay: date.Day,
|
|
IsLeapMonth: false,
|
|
LunarDateZh: "\u65e5\u671f\u8d85\u51fa\u519c\u5386\u652f\u6301\u8303\u56f4",
|
|
LunarDateEn: date.ToString("yyyy-MM-dd", CultureInfo.InvariantCulture),
|
|
GanzhiYearZh: "-",
|
|
GanzhiYearEn: "-",
|
|
ZodiacZh: "-",
|
|
ZodiacEn: "-");
|
|
}
|
|
}
|
|
|
|
private static (int Month, bool IsLeapMonth) NormalizeLunarMonth(int lunarYear, int rawMonth)
|
|
{
|
|
var leapMonth = Calendar.GetLeapMonth(lunarYear);
|
|
if (leapMonth == 0)
|
|
{
|
|
return (rawMonth, false);
|
|
}
|
|
|
|
if (rawMonth == leapMonth)
|
|
{
|
|
return (rawMonth - 1, true);
|
|
}
|
|
|
|
if (rawMonth > leapMonth)
|
|
{
|
|
return (rawMonth - 1, false);
|
|
}
|
|
|
|
return (rawMonth, false);
|
|
}
|
|
|
|
private static string BuildLunarDateZh(int lunarMonth, int lunarDay, bool isLeapMonth)
|
|
{
|
|
var monthName = lunarMonth is >= 1 and <= 12
|
|
? LunarMonthsZh[lunarMonth - 1]
|
|
: lunarMonth.ToString(CultureInfo.InvariantCulture);
|
|
var leapPrefix = isLeapMonth ? "\u95f0" : string.Empty;
|
|
return $"{leapPrefix}{monthName}\u6708{BuildLunarDayZh(lunarDay)}";
|
|
}
|
|
|
|
private static string BuildLunarDateEn(int lunarMonth, int lunarDay, bool isLeapMonth)
|
|
{
|
|
var leapPrefix = isLeapMonth ? "Leap " : string.Empty;
|
|
return $"{leapPrefix}M{lunarMonth} D{lunarDay}";
|
|
}
|
|
|
|
private static string BuildLunarDayZh(int day)
|
|
{
|
|
if (day <= 0)
|
|
{
|
|
return day.ToString(CultureInfo.InvariantCulture);
|
|
}
|
|
|
|
if (day <= 10)
|
|
{
|
|
return day == 10 ? "\u521d\u5341" : $"\u521d{LunarDayDigitsZh[day - 1]}";
|
|
}
|
|
|
|
if (day < 20)
|
|
{
|
|
return $"\u5341{LunarDayDigitsZh[day - 11]}";
|
|
}
|
|
|
|
if (day == 20)
|
|
{
|
|
return "\u4e8c\u5341";
|
|
}
|
|
|
|
if (day < 30)
|
|
{
|
|
return $"\u5eff{LunarDayDigitsZh[day - 21]}";
|
|
}
|
|
|
|
if (day == 30)
|
|
{
|
|
return "\u4e09\u5341";
|
|
}
|
|
|
|
return day.ToString(CultureInfo.InvariantCulture);
|
|
}
|
|
}
|
|
|
|
public sealed record LunarCalendarInfo(
|
|
int LunarYear,
|
|
int LunarMonth,
|
|
int LunarDay,
|
|
bool IsLeapMonth,
|
|
string LunarDateZh,
|
|
string LunarDateEn,
|
|
string GanzhiYearZh,
|
|
string GanzhiYearEn,
|
|
string ZodiacZh,
|
|
string ZodiacEn);
|
|
|