| 1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291129212931294129512961297129812991300130113021303130413051306130713081309131013111312131313141315131613171318131913201321132213231324132513261327132813291330133113321333133413351336133713381339134013411342134313441345134613471348134913501351135213531354135513561357135813591360136113621363136413651366136713681369137013711372137313741375137613771378137913801381138213831384138513861387138813891390139113921393139413951396139713981399140014011402140314041405140614071408140914101411141214131414141514161417141814191420142114221423142414251426142714281429143014311432143314341435143614371438143914401441144214431444144514461447144814491450145114521453145414551456145714581459146014611462146314641465146614671468146914701471147214731474147514761477147814791480148114821483148414851486148714881489149014911492149314941495149614971498149915001501150215031504150515061507150815091510151115121513151415151516151715181519152015211522152315241525152615271528152915301531153215331534153515361537153815391540154115421543154415451546154715481549155015511552155315541555155615571558155915601561 |
- using System;
- using System.Collections.Generic;
- using System.Linq;
- namespace Masuit.Tools.DateTimeExt
- {
- /// <summary>
- /// 中国农历类 支持 1900.1.31日起至 2069.12.31日止的数据
- /// </summary>
- /// <remarks>
- /// 本程序使用数据来源于网上的万年历查询,并综合了一些其它数据
- /// </remarks>
- public class ChineseCalendar
- {
- private class ChineseCalendarException : Exception
- {
- public ChineseCalendarException(string msg) : base(msg)
- {
- }
- }
- #region 内部变量
- private DateTime _date;
- private readonly DateTime _datetime;
- private readonly int _cYear;
- private readonly int _cMonth;
- private readonly int _cDay;
- /// <summary>
- /// 当月是否闰月
- /// </summary>
- private readonly bool _cIsLeapMonth;
- /// <summary>
- /// 当年是否有闰月
- /// </summary>
- private readonly bool _cIsLeapYear;
- #endregion
- #region 基础数据
- private const int MinYear = 1900;
- private const int MaxYear = 2050;
- private static readonly DateTime MinDay = new DateTime(1900, 1, 30);
- private static readonly DateTime MaxDay = new DateTime(2049, 12, 31);
- private const int GanZhiStartYear = 1864; //干支计算起始年
- private static readonly DateTime GanZhiStartDay = new DateTime(1899, 12, 22); //起始日
- private const string HzNum = "零一二三四五六七八九";
- private const int AnimalStartYear = 1900; //1900年为鼠年
- private static readonly DateTime ChineseConstellationReferDay = new DateTime(2007, 9, 13); //28星宿参考值,本日为角
- /// <summary>
- /// 来源于网上的农历数据
- /// </summary>
- /// <remarks>
- /// 数据结构如下,共使用17位数据
- /// 第17位:表示闰月天数,0表示29天 1表示30天
- /// 第16位-第5位(共12位)表示12个月,其中第16位表示第一月,如果该月为30天则为1,29天为0
- /// 第4位-第1位(共4位)表示闰月是哪个月,如果当年没有闰月,则置0
- ///</remarks>
- private static readonly int[] LunarDateArray =
- {
- 0x04BD8,
- 0x04AE0,
- 0x0A570,
- 0x054D5,
- 0x0D260,
- 0x0D950,
- 0x16554,
- 0x056A0,
- 0x09AD0,
- 0x055D2,
- 0x04AE0,
- 0x0A5B6,
- 0x0A4D0,
- 0x0D250,
- 0x1D255,
- 0x0B540,
- 0x0D6A0,
- 0x0ADA2,
- 0x095B0,
- 0x14977,
- 0x04970,
- 0x0A4B0,
- 0x0B4B5,
- 0x06A50,
- 0x06D40,
- 0x1AB54,
- 0x02B60,
- 0x09570,
- 0x052F2,
- 0x04970,
- 0x06566,
- 0x0D4A0,
- 0x0EA50,
- 0x06E95,
- 0x05AD0,
- 0x02B60,
- 0x186E3,
- 0x092E0,
- 0x1C8D7,
- 0x0C950,
- 0x0D4A0,
- 0x1D8A6,
- 0x0B550,
- 0x056A0,
- 0x1A5B4,
- 0x025D0,
- 0x092D0,
- 0x0D2B2,
- 0x0A950,
- 0x0B557,
- 0x06CA0,
- 0x0B550,
- 0x15355,
- 0x04DA0,
- 0x0A5B0,
- 0x14573,
- 0x052B0,
- 0x0A9A8,
- 0x0E950,
- 0x06AA0,
- 0x0AEA6,
- 0x0AB50,
- 0x04B60,
- 0x0AAE4,
- 0x0A570,
- 0x05260,
- 0x0F263,
- 0x0D950,
- 0x05B57,
- 0x056A0,
- 0x096D0,
- 0x04DD5,
- 0x04AD0,
- 0x0A4D0,
- 0x0D4D4,
- 0x0D250,
- 0x0D558,
- 0x0B540,
- 0x0B6A0,
- 0x195A6,
- 0x095B0,
- 0x049B0,
- 0x0A974,
- 0x0A4B0,
- 0x0B27A,
- 0x06A50,
- 0x06D40,
- 0x0AF46,
- 0x0AB60,
- 0x09570,
- 0x04AF5,
- 0x04970,
- 0x064B0,
- 0x074A3,
- 0x0EA50,
- 0x06B58,
- 0x055C0,
- 0x0AB60,
- 0x096D5,
- 0x092E0,
- 0x0C960,
- 0x0D954,
- 0x0D4A0,
- 0x0DA50,
- 0x07552,
- 0x056A0,
- 0x0ABB7,
- 0x025D0,
- 0x092D0,
- 0x0CAB5,
- 0x0A950,
- 0x0B4A0,
- 0x0BAA4,
- 0x0AD50,
- 0x055D9,
- 0x04BA0,
- 0x0A5B0,
- 0x15176,
- 0x052B0,
- 0x0A930,
- 0x07954,
- 0x06AA0,
- 0x0AD50,
- 0x05B52,
- 0x04B60,
- 0x0A6E6,
- 0x0A4E0,
- 0x0D260,
- 0x0EA65,
- 0x0D530,
- 0x05AA0,
- 0x076A3,
- 0x096D0,
- 0x04BD7,
- 0x04AD0,
- 0x0A4D0,
- 0x1D0B6,
- 0x0D250,
- 0x0D520,
- 0x0DD45,
- 0x0B5A0,
- 0x056D0,
- 0x055B2,
- 0x049B0,
- 0x0A577,
- 0x0A4B0,
- 0x0AA50,
- 0x1B255,
- 0x06D20,
- 0x0ADA0,
- 0x14B63
- };
- /// <summary>
- /// 星座
- /// </summary>
- private static readonly string[] ConstellationName =
- {
- "白羊座",
- "金牛座",
- "双子座",
- "巨蟹座",
- "狮子座",
- "处女座",
- "天秤座",
- "天蝎座",
- "射手座",
- "摩羯座",
- "水瓶座",
- "双鱼座"
- };
- /// <summary>
- /// 二十四节气
- /// </summary>
- private static readonly string[] LunarHolidayName =
- {
- "小寒",
- "大寒",
- "立春",
- "雨水",
- "惊蛰",
- "春分",
- "清明",
- "谷雨",
- "立夏",
- "小满",
- "芒种",
- "夏至",
- "小暑",
- "大暑",
- "立秋",
- "处暑",
- "白露",
- "秋分",
- "寒露",
- "霜降",
- "立冬",
- "小雪",
- "大雪",
- "冬至"
- };
- /// <summary>
- /// 二十八星宿
- /// </summary>
- private static readonly string[] ChineseConstellationName =
- {
- //四 五 六 日 一 二 三
- "角木蛟",
- "亢金龙",
- "女土蝠",
- "房日兔",
- "心月狐",
- "尾火虎",
- "箕水豹",
- "斗木獬",
- "牛金牛",
- "氐土貉",
- "虚日鼠",
- "危月燕",
- "室火猪",
- "壁水獝",
- "奎木狼",
- "娄金狗",
- "胃土彘",
- "昴日鸡",
- "毕月乌",
- "觜火猴",
- "参水猿",
- "井木犴",
- "鬼金羊",
- "柳土獐",
- "星日马",
- "张月鹿",
- "翼火蛇",
- "轸水蚓"
- };
- #region 节气数据
- /// <summary>
- /// 节气数据
- /// </summary>
- private static readonly string[] SolarTerm =
- {
- "小寒",
- "大寒",
- "立春",
- "雨水",
- "惊蛰",
- "春分",
- "清明",
- "谷雨",
- "立夏",
- "小满",
- "芒种",
- "夏至",
- "小暑",
- "大暑",
- "立秋",
- "处暑",
- "白露",
- "秋分",
- "寒露",
- "霜降",
- "立冬",
- "小雪",
- "大雪",
- "冬至"
- };
- private static readonly int[] STermInfo =
- {
- 0,
- 21208,
- 42467,
- 63836,
- 85337,
- 107014,
- 128867,
- 150921,
- 173149,
- 195551,
- 218072,
- 240693,
- 263343,
- 285989,
- 308563,
- 331033,
- 353350,
- 375494,
- 397447,
- 419210,
- 440795,
- 462224,
- 483532,
- 504758
- };
- #endregion
- #region 农历相关数据
- private const string GanStr = "甲乙丙丁戊己庚辛壬癸";
- private const string ZhiStr = "子丑寅卯辰巳午未申酉戌亥";
- private const string AnimalStr = "鼠牛虎兔龙蛇马羊猴鸡狗猪";
- private const string NStr1 = "日一二三四五六七八九";
- private const string NStr2 = "初十廿卅";
- private static readonly string[] MonthString =
- {
- "出错",
- "正月",
- "二月",
- "三月",
- "四月",
- "五月",
- "六月",
- "七月",
- "八月",
- "九月",
- "十月",
- "十一月",
- "腊月"
- };
- #endregion
- #region 节日数据
- /// <summary>
- /// 自定义的工作日
- /// </summary>
- public static HashSet<DateTime> CustomWorkDays { get; } = new HashSet<DateTime>();
- /// <summary>
- /// 自定义的节假日
- /// </summary>
- public static Dictionary<DateTime, string> CustomHolidays { get; } = new Dictionary<DateTime, string>();
- /// <summary>
- /// 按公历计算的通用节假日
- /// </summary>
- private static HashSet<DateInfoStruct> SolarHolidayInfo { get; } = new HashSet<DateInfoStruct>
- {
- new DateInfoStruct(1, 1, 1, "元旦"),
- new DateInfoStruct(2, 2, 0, "世界湿地日"),
- new DateInfoStruct(2, 10, 0, "国际气象节"),
- new DateInfoStruct(2, 14, 0, "情人节"),
- new DateInfoStruct(3, 1, 0, "国际海豹日"),
- new DateInfoStruct(3, 5, 0, "学雷锋纪念日"),
- new DateInfoStruct(3, 8, 0, "妇女节"),
- new DateInfoStruct(3, 12, 0, "植树节 孙中山逝世纪念日"),
- new DateInfoStruct(3, 14, 0, "国际警察日"),
- new DateInfoStruct(3, 15, 0, "消费者权益日"),
- new DateInfoStruct(3, 17, 0, "中国国医节 国际航海日"),
- new DateInfoStruct(3, 21, 0, "世界森林日 消除种族歧视国际日 世界儿歌日"),
- new DateInfoStruct(3, 22, 0, "世界水日"),
- new DateInfoStruct(3, 24, 0, "世界防治结核病日"),
- new DateInfoStruct(4, 1, 0, "愚人节"),
- new DateInfoStruct(4, 5, 1, "清明节"),
- new DateInfoStruct(4, 7, 0, "世界卫生日"),
- new DateInfoStruct(4, 22, 0, "世界地球日"),
- new DateInfoStruct(5, 1, 1, "劳动节"),
- new DateInfoStruct(5, 4, 0, "青年节"),
- new DateInfoStruct(5, 8, 0, "世界红十字日"),
- new DateInfoStruct(5, 12, 0, "国际护士节"),
- new DateInfoStruct(5, 31, 0, "世界无烟日"),
- new DateInfoStruct(6, 1, 0, "国际儿童节"),
- new DateInfoStruct(6, 5, 0, "世界环境保护日"),
- new DateInfoStruct(6, 26, 0, "国际禁毒日"),
- new DateInfoStruct(7, 1, 0, "建党节 香港回归纪念 世界建筑日"),
- new DateInfoStruct(7, 11, 0, "世界人口日"),
- new DateInfoStruct(8, 1, 0, "建军节"),
- new DateInfoStruct(8, 8, 0, "中国男子节 父亲节"),
- new DateInfoStruct(8, 15, 0, "抗日战争胜利纪念"),
- new DateInfoStruct(9, 9, 0, " 逝世纪念"),
- new DateInfoStruct(9, 10, 0, "教师节"),
- new DateInfoStruct(9, 18, 0, "九·一八事变纪念日"),
- new DateInfoStruct(9, 20, 0, "国际爱牙日"),
- new DateInfoStruct(9, 27, 0, "世界旅游日"),
- new DateInfoStruct(9, 28, 0, "孔子诞辰"),
- new DateInfoStruct(10, 1, 7, "国庆节 国际音乐日"),
- new DateInfoStruct(10, 24, 0, "联合国日"),
- new DateInfoStruct(11, 10, 0, "世界青年节"),
- new DateInfoStruct(11, 12, 0, "孙中山诞辰纪念"),
- new DateInfoStruct(12, 1, 0, "世界艾滋病日"),
- new DateInfoStruct(12, 3, 0, "世界残疾人日"),
- new DateInfoStruct(12, 20, 0, "澳门回归纪念"),
- new DateInfoStruct(12, 24, 0, "平安夜"),
- new DateInfoStruct(12, 25, 0, "圣诞节"),
- new DateInfoStruct(12, 26, 0, " 诞辰纪念")
- };
- /// <summary>
- /// 按农历计算的通用节假日
- /// </summary>
- private static HashSet<DateInfoStruct> LunarHolidayInfo { get; } = new HashSet<DateInfoStruct>
- {
- new DateInfoStruct(1, 1, 6, "春节"),
- new DateInfoStruct(1, 15, 0, "元宵节"),
- new DateInfoStruct(5, 5, 1, "端午节"),
- new DateInfoStruct(7, 7, 0, "七夕情人节"),
- new DateInfoStruct(7, 15, 0, "中元节 盂兰盆节"),
- new DateInfoStruct(8, 15, 1, "中秋节"),
- new DateInfoStruct(9, 9, 0, "重阳节"),
- new DateInfoStruct(12, 8, 0, "腊八节"),
- new DateInfoStruct(12, 23, 0, "北方小年(扫房)"),
- new DateInfoStruct(12, 24, 0, "南方小年(掸尘)"),
- //new HolidayStruct(12, 30, 0, "除夕") //注意除夕需要其它方法进行计算
- };
- private static readonly WeekHolidayStruct[] WHolidayInfo =
- {
- new WeekHolidayStruct(5, 2, 1, "母亲节"),
- new WeekHolidayStruct(5, 3, 1, "全国助残日"),
- new WeekHolidayStruct(6, 3, 1, "父亲节"),
- new WeekHolidayStruct(9, 3, 3, "国际和平日"),
- new WeekHolidayStruct(9, 4, 1, "国际聋人节"),
- new WeekHolidayStruct(10, 1, 2, "国际住房日"),
- new WeekHolidayStruct(10, 1, 4, "国际减轻自然灾害日"),
- new WeekHolidayStruct(11, 4, 5, "感恩节")
- };
- #endregion
- #endregion
- /// <summary>
- /// 用一个标准的公历日期来初使化
- /// </summary>
- /// <param name="dt"></param>
- public ChineseCalendar(DateTime dt)
- {
- int i;
- CheckDateLimit(dt);
- _date = dt.Date;
- _datetime = dt;
- //农历日期计算部分
- var temp = 0;
- TimeSpan ts = _date - MinDay; //计算两天的基本差距
- var offset = ts.Days;
- for (i = MinYear; i <= MaxYear; i++)
- {
- temp = GetChineseYearDays(i); //求当年农历年天数
- if (offset - temp < 1)
- {
- break;
- }
- offset = offset - temp;
- }
- _cYear = i;
- var leap = GetChineseLeapMonth(_cYear);
- //设定当年是否有闰月
- _cIsLeapYear = leap > 0;
- _cIsLeapMonth = false;
- for (i = 1; i <= 12; i++)
- {
- //闰月
- if (leap > 0 && i == leap + 1 && _cIsLeapMonth == false)
- {
- _cIsLeapMonth = true;
- i = i - 1;
- temp = GetChineseLeapMonthDays(_cYear); //计算闰月天数
- }
- else
- {
- _cIsLeapMonth = false;
- temp = GetChineseMonthDays(_cYear, i); //计算非闰月天数
- }
- offset = offset - temp;
- if (offset <= 0)
- {
- break;
- }
- }
- offset = offset + temp;
- _cMonth = i;
- _cDay = offset;
- }
- /// <summary>
- /// 用农历的日期来初使化
- /// </summary>
- /// <param name="cy">农历年</param>
- /// <param name="cm">农历月</param>
- /// <param name="cd">农历日</param>
- /// <param name="leapMonthFlag">闰月标志</param>
- public ChineseCalendar(int cy, int cm, int cd, bool leapMonthFlag)
- {
- int i, temp;
- CheckChineseDateLimit(cy, cm, cd, leapMonthFlag);
- _cYear = cy;
- _cMonth = cm;
- _cDay = cd;
- var offset = 0;
- for (i = MinYear; i < cy; i++)
- {
- temp = GetChineseYearDays(i); //求当年农历年天数
- offset = offset + temp;
- }
- var leap = GetChineseLeapMonth(cy);
- _cIsLeapYear = leap != 0;
- _cIsLeapMonth = cm == leap && leapMonthFlag;
- if (_cIsLeapYear == false || cm < leap) //当年没有闰月||计算月份小于闰月
- {
- for (i = 1; i < cm; i++)
- {
- temp = GetChineseMonthDays(cy, i); //计算非闰月天数
- offset = offset + temp;
- }
- //检查日期是否大于最大天
- if (cd > GetChineseMonthDays(cy, cm))
- {
- throw new ChineseCalendarException("不合法的农历日期");
- }
- offset = offset + cd; //加上当月的天数
- }
- else //是闰年,且计算月份大于或等于闰月
- {
- for (i = 1; i < cm; i++)
- {
- temp = GetChineseMonthDays(cy, i); //计算非闰月天数
- offset = offset + temp;
- }
- if (cm > leap) //计算月大于闰月
- {
- temp = GetChineseLeapMonthDays(cy); //计算闰月天数
- offset = offset + temp; //加上闰月天数
- if (cd > GetChineseMonthDays(cy, cm))
- {
- throw new ChineseCalendarException("不合法的农历日期");
- }
- offset = offset + cd;
- }
- else //计算月等于闰月
- {
- //如果需要计算的是闰月,则应首先加上与闰月对应的普通月的天数
- if (_cIsLeapMonth) //计算月为闰月
- {
- temp = GetChineseMonthDays(cy, cm); //计算非闰月天数
- offset = offset + temp;
- }
- if (cd > GetChineseLeapMonthDays(cy))
- {
- throw new ChineseCalendarException("不合法的农历日期");
- }
- offset = offset + cd;
- }
- }
- _date = MinDay.AddDays(offset);
- }
- #region 私有函数
- /// <summary>
- /// 传回农历 y年m月的总天数
- /// </summary>
- /// <param name="year"></param>
- /// <param name="month"></param>
- /// <returns></returns>
- private int GetChineseMonthDays(int year, int month)
- {
- if (BitTest32((LunarDateArray[year - MinYear] & 0x0000FFFF), (16 - month)))
- {
- return 30;
- }
- return 29;
- }
- /// <summary>
- /// 传回农历 y年闰哪个月 1-12 , 没闰传回 0
- /// </summary>
- /// <param name="year"></param>
- /// <returns></returns>
- private int GetChineseLeapMonth(int year)
- {
- return LunarDateArray[year - MinYear] & 0xF;
- }
- /// <summary>
- /// 传回农历 y年闰月的天数
- /// </summary>
- /// <param name="year"></param>
- /// <returns></returns>
- private int GetChineseLeapMonthDays(int year)
- {
- if (GetChineseLeapMonth(year) != 0)
- {
- return (LunarDateArray[year - MinYear] & 0x10000) != 0 ? 30 : 29;
- }
- return 0;
- }
- /// <summary>
- /// 取农历年一年的天数
- /// </summary>
- /// <param name="year"></param>
- /// <returns></returns>
- private int GetChineseYearDays(int year)
- {
- var sumDay = 348;
- var i = 0x8000;
- var info = LunarDateArray[year - MinYear] & 0x0FFFF;
- //计算12个月中有多少天为30天
- for (int m = 0; m < 12; m++)
- {
- var f = info & i;
- if (f != 0)
- {
- sumDay++;
- }
- i = i >> 1;
- }
- return sumDay + GetChineseLeapMonthDays(year);
- }
- /// <summary>
- /// 获得当前时间的时辰
- /// </summary>
- /// <param name="dt"></param>
- /// <returns></returns>
- ///
- private string GetChineseHour(DateTime dt)
- {
- //计算时辰的地支
- var hour = dt.Hour;
- var minute = dt.Minute;
- if (minute != 0) hour += 1;
- var offset = hour / 2;
- if (offset >= 12) offset = 0;
- //计算天干
- TimeSpan ts = _date - GanZhiStartDay;
- var i = ts.Days % 60;
- var indexGan = ((i % 10 + 1) * 2 - 1) % 10 - 1;
- var tmpGan = GanStr.Substring(indexGan) + GanStr.Substring(0, indexGan + 2);
- return tmpGan[offset].ToString() + ZhiStr[offset];
- }
- /// <summary>
- /// 检查公历日期是否符合要求
- /// </summary>
- /// <param name="dt"></param>
- private void CheckDateLimit(DateTime dt)
- {
- if ((dt < MinDay) || (dt > MaxDay))
- {
- throw new ChineseCalendarException("超出可转换的日期");
- }
- }
- /// <summary>
- /// 检查农历日期是否合理
- /// </summary>
- /// <param name="year"></param>
- /// <param name="month"></param>
- /// <param name="day"></param>
- /// <param name="leapMonth"></param>
- private void CheckChineseDateLimit(int year, int month, int day, bool leapMonth)
- {
- if ((year < MinYear) || (year > MaxYear))
- {
- throw new ChineseCalendarException("非法农历日期");
- }
- if ((month < 1) || (month > 12))
- {
- throw new ChineseCalendarException("非法农历日期");
- }
- if ((day < 1) || (day > 30)) //中国的月最多30天
- {
- throw new ChineseCalendarException("非法农历日期");
- }
- int leap = GetChineseLeapMonth(year); // 计算该年应该闰哪个月
- if (leapMonth && month != leap)
- {
- throw new ChineseCalendarException("非法农历日期");
- }
- }
- /// <summary>
- /// 将0-9转成汉字形式
- /// </summary>
- /// <param name="n"></param>
- /// <returns></returns>
- private string ConvertNumToChineseNum(char n)
- {
- if ((n < '0') || (n > '9')) return "";
- switch (n)
- {
- case '0':
- return HzNum[0].ToString();
- case '1':
- return HzNum[1].ToString();
- case '2':
- return HzNum[2].ToString();
- case '3':
- return HzNum[3].ToString();
- case '4':
- return HzNum[4].ToString();
- case '5':
- return HzNum[5].ToString();
- case '6':
- return HzNum[6].ToString();
- case '7':
- return HzNum[7].ToString();
- case '8':
- return HzNum[8].ToString();
- case '9':
- return HzNum[9].ToString();
- default:
- return "";
- }
- }
- /// <summary>
- /// 测试某位是否为真
- /// </summary>
- /// <param name="num"></param>
- /// <param name="bitpostion"></param>
- /// <returns></returns>
- private bool BitTest32(int num, int bitpostion)
- {
- if ((bitpostion > 31) || (bitpostion < 0))
- throw new Exception("Error Param: bitpostion[0-31]:" + bitpostion.ToString());
- int bit = 1 << bitpostion;
- if ((num & bit) == 0)
- {
- return false;
- }
- else
- {
- return true;
- }
- }
- /// <summary>
- /// 将星期几转成数字表示
- /// </summary>
- /// <param name="dayOfWeek"></param>
- /// <returns></returns>
- private int ConvertDayOfWeek(DayOfWeek dayOfWeek)
- {
- switch (dayOfWeek)
- {
- case DayOfWeek.Sunday:
- return 1;
- case DayOfWeek.Monday:
- return 2;
- case DayOfWeek.Tuesday:
- return 3;
- case DayOfWeek.Wednesday:
- return 4;
- case DayOfWeek.Thursday:
- return 5;
- case DayOfWeek.Friday:
- return 6;
- case DayOfWeek.Saturday:
- return 7;
- default:
- return 0;
- }
- }
- /// <summary>
- /// 比较当天是不是指定的第周几
- /// </summary>
- /// <param name="date"></param>
- /// <param name="month"></param>
- /// <param name="week"></param>
- /// <param name="day"></param>
- /// <returns></returns>
- private bool CompareWeekDayHoliday(DateTime date, int month, int week, int day)
- {
- bool ret = false;
- if (date.Month == month) //月份相同
- {
- if (ConvertDayOfWeek(date.DayOfWeek) == day) //星期几相同
- {
- DateTime firstDay = new DateTime(date.Year, date.Month, 1); //生成当月第一天
- int i = ConvertDayOfWeek(firstDay.DayOfWeek);
- int firWeekDays = 7 - ConvertDayOfWeek(firstDay.DayOfWeek) + 1; //计算第一周剩余天数
- if (i > day)
- {
- if ((week - 1) * 7 + day + firWeekDays == date.Day)
- {
- ret = true;
- }
- }
- else
- {
- if (day + firWeekDays + (week - 2) * 7 == date.Day)
- {
- ret = true;
- }
- }
- }
- }
- return ret;
- }
- #endregion
- #region 节日
- /// <summary>
- /// 计算中国农历节日
- /// </summary>
- public string ChineseCalendarHoliday
- {
- get
- {
- string tempStr = "";
- if (_cIsLeapMonth)
- {
- return tempStr;
- }
- foreach (DateInfoStruct lh in LunarHolidayInfo)
- {
- var end = lh.Recess > 0 ? lh.Day + lh.Recess - 1 : lh.Day + lh.Recess;
- if (lh.Month == _cMonth && _cDay >= lh.Day && _cDay <= end)
- {
- tempStr = lh.HolidayName;
- break;
- }
- }
- //对除夕进行特别处理
- if (_cMonth != 12)
- {
- return tempStr;
- }
- int i = GetChineseMonthDays(_cYear, 12); //计算当年农历12月的总天数
- if (_cDay == i) //如果为最后一天
- {
- tempStr = "除夕";
- }
- return tempStr;
- }
- }
- /// <summary>
- /// 按某月第几周第几日计算的节日
- /// </summary>
- public string WeekDayHoliday
- {
- get
- {
- string tempStr = "";
- foreach (WeekHolidayStruct wh in WHolidayInfo)
- {
- if (!CompareWeekDayHoliday(_date, wh.Month, wh.WeekAtMonth, wh.WeekDay))
- {
- continue;
- }
- tempStr = wh.HolidayName;
- break;
- }
- return tempStr;
- }
- }
- /// <summary>
- /// 按公历日计算的节日
- /// </summary>
- public string DateHoliday
- {
- get
- {
- string tempStr = "";
- foreach (DateInfoStruct sh in SolarHolidayInfo)
- {
- var end = sh.Recess > 0 ? sh.Day + sh.Recess - 1 : sh.Day + sh.Recess;
- if ((sh.Month == _date.Month) && _date.Day >= sh.Day && _date.Day <= end)
- {
- tempStr = sh.HolidayName;
- break;
- }
- if (CustomHolidays.Keys.Any(d => d.Date == _date))
- {
- tempStr = CustomHolidays[_date];
- break;
- }
- }
- return tempStr;
- }
- }
- public bool IsHoliday => !IsWorkDay;
- public bool IsWorkDay
- {
- get
- {
- bool isHoliday = false;
- foreach (DateInfoStruct sh in SolarHolidayInfo)
- {
- var end = sh.Recess > 0 ? sh.Day + sh.Recess - 1 : sh.Day + sh.Recess;
- if ((sh.Month == _date.Month) && _date.Day >= sh.Day && _date.Day <= end && sh.Recess > 0)
- {
- isHoliday = true;
- break;
- }
- if (CustomHolidays.Keys.Any(d => d.Date == _date))
- {
- isHoliday = true;
- break;
- }
- }
- if (!isHoliday)
- {
- foreach (DateInfoStruct lh in LunarHolidayInfo)
- {
- var end = lh.Recess > 0 ? lh.Day + lh.Recess - 1 : lh.Day + lh.Recess;
- if (lh.Month == _cMonth && _cDay >= lh.Day && _cDay <= end && lh.Recess > 0)
- {
- isHoliday = true;
- break;
- }
- }
- //对除夕进行特别处理
- if (_cMonth == 12)
- {
- int i = GetChineseMonthDays(_cYear, 12); //计算当年农历12月的总天数
- if (_cDay == i) //如果为最后一天
- {
- isHoliday = true;
- }
- }
- }
- return !isHoliday && !IsWeekend() || CustomWorkDays.Any(s => s.Date == _date);
- }
- }
- /// <summary>
- /// 是否是周末
- /// </summary>
- /// <returns></returns>
- private bool IsWeekend()
- {
- return _date.DayOfWeek == DayOfWeek.Saturday || _date.DayOfWeek == DayOfWeek.Sunday;
- }
- #endregion
- #region 公历日期
- /// <summary>
- /// 取对应的公历日期
- /// </summary>
- public DateTime Date
- {
- get => _date;
- set => _date = value;
- }
- /// <summary>
- /// 取星期几
- /// </summary>
- public DayOfWeek WeekDay => _date.DayOfWeek;
- /// <summary>
- /// 周几的字符
- /// </summary>
- public string WeekDayStr
- {
- get
- {
- switch (_date.DayOfWeek)
- {
- case DayOfWeek.Sunday:
- return "星期日";
- case DayOfWeek.Monday:
- return "星期一";
- case DayOfWeek.Tuesday:
- return "星期二";
- case DayOfWeek.Wednesday:
- return "星期三";
- case DayOfWeek.Thursday:
- return "星期四";
- case DayOfWeek.Friday:
- return "星期五";
- default:
- return "星期六";
- }
- }
- }
- /// <summary>
- /// 公历日期中文表示法 如一九九七年七月一日
- /// </summary>
- public string DateString => "公元" + _date.ToLongDateString();
- /// <summary>
- /// 当前是否公历闰年
- /// </summary>
- public bool IsLeapYear => DateTime.IsLeapYear(_date.Year);
- /// <summary>
- /// 28星宿计算
- /// </summary>
- public string ChineseConstellation
- {
- get
- {
- TimeSpan ts = _date - ChineseConstellationReferDay;
- var offset = ts.Days;
- var modStarDay = offset % 28;
- return (modStarDay >= 0 ? ChineseConstellationName[modStarDay] : ChineseConstellationName[27 + modStarDay]);
- }
- }
- /// <summary>
- /// 时辰
- /// </summary>
- public string ChineseHour => GetChineseHour(_datetime);
- #endregion
- #region 农历日期
- /// <summary>
- /// 农历今天
- /// </summary>
- public static ChineseCalendar Today => new ChineseCalendar(DateTime.Today);
- /// <summary>
- /// 是否闰月
- /// </summary>
- public bool IsChineseLeapMonth => _cIsLeapMonth;
- /// <summary>
- /// 当年是否有闰月
- /// </summary>
- public bool IsChineseLeapYear => _cIsLeapYear;
- /// <summary>
- /// 农历日
- /// </summary>
- public int ChineseDay => _cDay;
- /// <summary>
- /// 农历日中文表示
- /// </summary>
- public string ChineseDayString
- {
- get
- {
- switch (_cDay)
- {
- case 0:
- return "";
- case 10:
- return "初十";
- case 20:
- return "二十";
- case 30:
- return "三十";
- default:
- return NStr2[_cDay / 10] + NStr1[_cDay % 10].ToString();
- }
- }
- }
- /// <summary>
- /// 农历的月份
- /// </summary>
- public int ChineseMonth => _cMonth;
- /// <summary>
- /// 农历月份字符串
- /// </summary>
- public string ChineseMonthString => MonthString[_cMonth];
- /// <summary>
- /// 取农历年份
- /// </summary>
- public int ChineseYear => _cYear;
- /// <summary>
- /// 取农历年字符串如,一九九七年
- /// </summary>
- public string ChineseYearString
- {
- get
- {
- string tempStr = "";
- string num = _cYear.ToString();
- for (int i = 0; i < 4; i++)
- {
- tempStr += ConvertNumToChineseNum(num[i]);
- }
- return tempStr + "年";
- }
- }
- /// <summary>
- /// 取农历日期表示法:农历一九九七年正月初五
- /// </summary>
- public string ChineseDateString
- {
- get
- {
- if (_cIsLeapMonth)
- {
- return ChineseYearString + "闰" + ChineseMonthString + ChineseDayString;
- }
- return ChineseYearString + ChineseMonthString + ChineseDayString;
- }
- }
- /// <summary>
- /// 定气法计算二十四节气,二十四节气是按地球公转来计算的,并非是阴历计算的
- /// </summary>
- /// <remarks>
- /// 节气的定法有两种。古代历法采用的称为"恒气",即按时间把一年等分为24份,
- /// 每一节气平均得15天有余,所以又称"平气"。现代农历采用的称为"定气",即
- /// 按地球在轨道上的位置为标准,一周360°,两节气之间相隔15°。由于冬至时地
- /// 球位于近日点附近,运动速度较快,因而太阳在黄道上移动15°的时间不到15天。
- /// 夏至前后的情况正好相反,太阳在黄道上移动较慢,一个节气达16天之多。采用
- /// 定气时可以保证春、秋两分必然在昼夜平分的那两天。
- /// </remarks>
- public string ChineseTwentyFourDay
- {
- get
- {
- DateTime baseDateAndTime = new DateTime(1900, 1, 6, 2, 5, 0); //#1/6/1900 2:05:00 AM#
- string tempStr = "";
- var y = _date.Year;
- for (int i = 1; i <= 24; i++)
- {
- var num = 525948.76 * (y - 1900) + STermInfo[i - 1];
- var newDate = baseDateAndTime.AddMinutes(num);
- if (newDate.DayOfYear != _date.DayOfYear)
- {
- continue;
- }
- tempStr = SolarTerm[i - 1];
- break;
- }
- return tempStr;
- }
- }
- //当前日期前一个最近节气
- public string ChineseTwentyFourPrevDay
- {
- get
- {
- DateTime baseDateAndTime = new DateTime(1900, 1, 6, 2, 5, 0); //#1/6/1900 2:05:00 AM#
- string tempStr = "";
- var y = _date.Year;
- for (int i = 24; i >= 1; i--)
- {
- var num = 525948.76 * (y - 1900) + STermInfo[i - 1];
- var newDate = baseDateAndTime.AddMinutes(num);
- if (newDate.DayOfYear < _date.DayOfYear)
- {
- tempStr = $"{SolarTerm[i - 1]}[{newDate:yyyy-MM-dd}]";
- break;
- }
- }
- return tempStr;
- }
- }
- //当前日期后一个最近节气
- public string ChineseTwentyFourNextDay
- {
- get
- {
- DateTime baseDateAndTime = new DateTime(1900, 1, 6, 2, 5, 0); //#1/6/1900 2:05:00 AM#
- string tempStr = "";
- var y = _date.Year;
- for (int i = 1; i <= 24; i++)
- {
- var num = 525948.76 * (y - 1900) + STermInfo[i - 1];
- var newDate = baseDateAndTime.AddMinutes(num);
- if (newDate.DayOfYear > _date.DayOfYear)
- {
- tempStr = $"{SolarTerm[i - 1]}[{newDate:yyyy-MM-dd}]";
- break;
- }
- }
- return tempStr;
- }
- }
- #endregion
- #region 星座
- /// <summary>
- /// 计算指定日期的星座序号
- /// </summary>
- /// <returns></returns>
- public string Constellation
- {
- get
- {
- int index;
- var m = _date.Month;
- var d = _date.Day;
- var y = m * 100 + d;
- if (((y >= 321) && (y <= 419)))
- {
- index = 0;
- }
- else if ((y >= 420) && (y <= 520))
- {
- index = 1;
- }
- else if ((y >= 521) && (y <= 620))
- {
- index = 2;
- }
- else if ((y >= 621) && (y <= 722))
- {
- index = 3;
- }
- else if ((y >= 723) && (y <= 822))
- {
- index = 4;
- }
- else if ((y >= 823) && (y <= 922))
- {
- index = 5;
- }
- else if ((y >= 923) && (y <= 1022))
- {
- index = 6;
- }
- else if ((y >= 1023) && (y <= 1121))
- {
- index = 7;
- }
- else if ((y >= 1122) && (y <= 1221))
- {
- index = 8;
- }
- else if ((y >= 1222) || (y <= 119))
- {
- index = 9;
- }
- else if ((y >= 120) && (y <= 218))
- {
- index = 10;
- }
- else if ((y >= 219) && (y <= 320))
- {
- index = 11;
- }
- else
- {
- index = 0;
- }
- return ConstellationName[index];
- }
- }
- #endregion
- #region 属相
- /// <summary>
- /// 计算属相的索引,注意虽然属相是以农历年来区别的,但是目前在实际使用中是按公历来计算的
- /// 鼠年为1,其它类推
- /// </summary>
- public int Animal
- {
- get
- {
- int offset = _date.Year - AnimalStartYear;
- return (offset % 12) + 1;
- }
- }
- /// <summary>
- /// 取属相字符串
- /// </summary>
- public string AnimalString
- {
- get
- {
- int offset = _date.Year - AnimalStartYear; //阳历计算
- return AnimalStr[offset % 12].ToString();
- }
- }
- #endregion
- #region 天干地支
- /// <summary>
- /// 取农历年的干支表示法如 乙丑年
- /// </summary>
- public string GanZhiYearString
- {
- get
- {
- int i = (_cYear - GanZhiStartYear) % 60; //计算干支
- var tempStr = GanStr[i % 10] + ZhiStr[i % 12].ToString() + "年";
- return tempStr;
- }
- }
- /// <summary>
- /// 取干支的月表示字符串,注意农历的闰月不记干支
- /// </summary>
- public string GanZhiMonthString
- {
- get
- {
- //每个月的地支总是固定的,而且总是从寅月开始
- int zhiIndex;
- if (_cMonth > 10)
- {
- zhiIndex = _cMonth - 10;
- }
- else
- {
- zhiIndex = _cMonth + 2;
- }
- var zhi = ZhiStr[zhiIndex - 1].ToString();
- //根据当年的干支年的干来计算月干的第一个
- int ganIndex = 1;
- int i = (_cYear - GanZhiStartYear) % 60; //计算干支
- switch (i % 10)
- {
- case 0: //甲
- ganIndex = 3;
- break;
- case 1: //乙
- ganIndex = 5;
- break;
- case 2: //丙
- ganIndex = 7;
- break;
- case 3: //丁
- ganIndex = 9;
- break;
- case 4: //戊
- ganIndex = 1;
- break;
- case 5: //己
- ganIndex = 3;
- break;
- case 6: //庚
- ganIndex = 5;
- break;
- case 7: //辛
- ganIndex = 7;
- break;
- case 8: //壬
- ganIndex = 9;
- break;
- case 9: //癸
- ganIndex = 1;
- break;
- }
- var gan = GanStr[(ganIndex + _cMonth - 2) % 10].ToString();
- return gan + zhi + "月";
- }
- }
- /// <summary>
- /// 取干支日表示法
- /// </summary>
- public string GanZhiDayString
- {
- get
- {
- TimeSpan ts = _date - GanZhiStartDay;
- var offset = ts.Days;
- var i = offset % 60;
- return GanStr[i % 10].ToString() + ZhiStr[i % 12] + "日";
- }
- }
- /// <summary>
- /// 取当前日期的干支表示法如 甲子年乙丑月丙庚日
- /// </summary>
- public string GanZhiDateString => GanZhiYearString + GanZhiMonthString + GanZhiDayString;
- #endregion
- /// <summary>
- /// 取下一天
- /// </summary>
- /// <returns></returns>
- public ChineseCalendar NextDay()
- {
- DateTime nextDay = _date.AddDays(1);
- return new ChineseCalendar(nextDay);
- }
- /// <summary>
- /// 取前一天
- /// </summary>
- /// <returns></returns>
- public ChineseCalendar PervDay()
- {
- DateTime pervDay = _date.AddDays(-1);
- return new ChineseCalendar(pervDay);
- }
- /// <summary>
- /// 取下n天
- /// </summary>
- /// <returns></returns>
- public ChineseCalendar AddDays(int days)
- {
- DateTime nextDay = _date.AddDays(days);
- return new ChineseCalendar(nextDay);
- }
- /// <summary>
- /// 取下n天
- /// </summary>
- /// <returns></returns>
- public ChineseCalendar AddWorkDays(int days)
- {
- var cc = new ChineseCalendar(_date);
- while (true)
- {
- cc = cc.AddDays(1);
- if (cc.IsWorkDay)
- {
- days--;
- }
- else
- {
- Console.WriteLine("阳历:" + cc.DateString);
- Console.WriteLine("节日:" + cc.DateHoliday);
- Console.WriteLine("农历节日:" + cc.ChineseCalendarHoliday);
- Console.WriteLine("星期:" + cc.WeekDayStr);
- Console.WriteLine("----------------------------");
- }
- if (days == 0)
- {
- return cc;
- }
- }
- }
- /// <summary>
- /// 加n月
- /// </summary>
- /// <returns></returns>
- public ChineseCalendar AddMonths(int months)
- {
- DateTime nextDay = _date.AddMonths(months);
- return new ChineseCalendar(nextDay);
- }
- }
- }
|