1 public class ChineseCalendarException : Exception 2 { 3 public ChineseCalendarException(string msg) 4 : base(msg) 5 { 6 } 7 }
1 public class ChineseCalendar 2 { 3 #region 内部结构 4 5 private struct LunarHolidayStruct 6 { 7 public int Day; 8 public string HolidayName; 9 public int Month; 10 public int Recess; 11 12 public LunarHolidayStruct(int month, int day, int recess, string name) 13 { 14 Month = month; 15 Day = day; 16 Recess = recess; 17 HolidayName = name; 18 } 19 } 20 21 private struct SolarHolidayStruct 22 { 23 public int Day; 24 public string HolidayName; 25 public int Month; 26 public int Recess; //假期长度 27 28 public SolarHolidayStruct(int month, int day, int recess, string name) 29 { 30 Month = month; 31 Day = day; 32 Recess = recess; 33 HolidayName = name; 34 } 35 } 36 37 private struct WeekHolidayStruct 38 { 39 public string HolidayName; 40 public int Month; 41 public int WeekAtMonth; 42 public int WeekDay; 43 44 public WeekHolidayStruct(int month, int weekAtMonth, int weekDay, string name) 45 { 46 Month = month; 47 WeekAtMonth = weekAtMonth; 48 WeekDay = weekDay; 49 HolidayName = name; 50 } 51 } 52 53 #endregion 54 55 #region 内部变量 56 57 private static ChineseLunisolarCalendar calendar = new ChineseLunisolarCalendar(); 58 59 private string _animal; 60 private string _lunarDayString; 61 private string _lunarMonthString; 62 private string _lunarYearString; 63 private string _sexagenary; 64 private DateTime _solarDate; 65 66 #endregion 67 68 #region 基础数据 69 70 #region 基本常量 71 72 private const int GanZhiStartYear = 1864; //干支计算起始年 73 private const string ChineseNumber = "〇一二三四五六七八九"; 74 private const int AnimalStartYear = 1900; //1900年为鼠年 75 private static DateTime GanZhiStartDay = new DateTime(1899, 12, 22); //起始日 76 private static DateTime ChineseConstellationReferDay = new DateTime(2007, 9, 13); //28星宿参考值,本日为角 77 public readonly int maxLunarYear = 2100; 78 public readonly DateTime maxSolarDate = new DateTime(2101, 1, 28); //最大公历日期 79 public readonly int minLunarYear = 1901; 80 public readonly DateTime minSolarDate = new DateTime(1901, 2, 19); //最小公历日期 81 82 #endregion 83 84 #region 星座和诞生石 85 86 private static readonly string[] ConstellationName = 87 { 88 "白羊座", "金牛座", "双子座", 89 "巨蟹座", "狮子座", "处女座", 90 "天秤座", "天蝎座", "射手座", 91 "摩羯座", "水瓶座", "双鱼座" 92 }; 93 94 private static readonly string[] BirthStoneName = 95 { 96 "钻石", "蓝宝石", "玛瑙", "珍珠", "红宝石", "红条纹玛瑙", 97 "蓝宝石", "猫眼石", "黄宝石", "土耳其玉", "紫水晶", "月长石,血石" 98 }; 99 100 //白羊座: 3月21日------4月20日 诞生石: 钻石 101 //金牛座: 4月21日------5月20日 诞生石: 蓝宝石 102 //双子座: 5月21日------6月21日 诞生石: 玛瑙 103 //巨蟹座: 6月22日------7月22日 诞生石: 珍珠 104 //狮子座: 7月23日------8月22日 诞生石: 红宝石 105 //处女座: 8月23日------9月22日 诞生石: 红条纹玛瑙 106 //天秤座: 9月23日-----10月23日 诞生石: 蓝宝石 107 //天蝎座: 10月24日-----11月21日 诞生石: 猫眼石 108 //射手座: 11月22日-----12月21日 诞生石: 黄宝石 109 //摩羯座: 12月22日------1月19日 诞生石: 土耳其玉 110 //水瓶座: 1月20日------2月18日 诞生石: 紫水晶 111 //双鱼座: 2月19日------3月20日 诞生石: 月长石,血石 112 113 #endregion 114 115 #region 二十八星宿 116 117 private static readonly string[] ChineseConstellationName = 118 { 119 //四 五 六 日 一 二 三 120 "角木蛟", "亢金龙", "女土蝠", "房日兔", "心月狐", "尾火虎", "箕水豹", 121 "斗木獬", "牛金牛", "氐土貉", "虚日鼠", "危月燕", "室火猪", "壁水獝", 122 "奎木狼", "娄金狗", "胃土彘", "昴日鸡", "毕月乌", "觜火猴", "参水猿", 123 "井木犴", "鬼金羊", "柳土獐", "星日马", "张月鹿", "翼火蛇", "轸水蚓" 124 }; 125 126 #endregion 127 128 #region 二十四节气 129 130 private static readonly string[] SolarTerms = 131 { 132 "小寒", "大寒", "立春", "雨水", "惊蛰", "春分", 133 "清明", "谷雨", "立夏", "小满", "芒种", "夏至", 134 "小暑", "大暑", "立秋", "处暑", "白露", "秋分", 135 "寒露", "霜降", "立冬", "小雪", "大雪", "冬至" 136 }; 137 138 private static readonly int[] sTermsInfo = 139 { 140 0, 21208, 42467, 63836, 85337, 107014, 141 128867, 150921, 173149, 195551, 218072, 240693, 142 263343, 285989, 308563, 331033, 353350, 375494, 143 397447, 419210, 440795, 462224, 483532, 504758 144 }; 145 146 #endregion 147 148 #region 农历相关数据 149 150 private const string CelestialStem = "甲乙丙丁戊己庚辛壬癸"; 151 private const string TerrestrialBranch = "子丑寅卯辰巳午未申酉戌亥"; 152 private const string AnimalsStr = "鼠牛虎兔龙蛇马羊猴鸡狗猪"; 153 154 private static readonly string[] ChineseDayName = 155 { 156 "初一", "初二", "初三", "初四", "初五", "初六", "初七", "初八", "初九", "初十", 157 "十一", "十二", "十三", "十四", "十五", "十六", "十七", "十八", "十九", "二十", 158 "廿一", "廿二", "廿三", "廿四", "廿五", "廿六", "廿七", "廿八", "廿九", "三十" 159 }; 160 161 private static readonly string[] ChineseMonthName = 162 { 163 "正", "二", "三", "四", "五", "六", "七", "八", "九", "十", "冬", "腊" 164 }; 165 166 #endregion 167 168 #region 按公历计算的节日 169 170 private static readonly SolarHolidayStruct[] solarHolidayInfo = 171 { 172 new SolarHolidayStruct(1, 1, 1, "元旦"), 173 new SolarHolidayStruct(2, 2, 0, "世界湿地日"), 174 new SolarHolidayStruct(2, 10, 0, "国际气象节"), 175 new SolarHolidayStruct(2, 14, 0, "情人节"), 176 new SolarHolidayStruct(3, 1, 0, "国际海豹日"), 177 new SolarHolidayStruct(3, 5, 0, "学雷锋纪念日"), 178 new SolarHolidayStruct(3, 8, 0, "妇女节"), 179 new SolarHolidayStruct(3, 12, 0, "植树节 孙中山逝世纪念日"), 180 new SolarHolidayStruct(3, 14, 0, "国际警察日"), 181 new SolarHolidayStruct(3, 15, 0, "消费者权益日"), 182 new SolarHolidayStruct(3, 17, 0, "中国国医节 国际航海日"), 183 new SolarHolidayStruct(3, 21, 0, "世界森林日 消除种族歧视国际日 世界儿歌日"), 184 new SolarHolidayStruct(3, 22, 0, "世界水日"), 185 new SolarHolidayStruct(3, 24, 0, "世界防治结核病日"), 186 new SolarHolidayStruct(4, 1, 0, "愚人节"), 187 new SolarHolidayStruct(4, 7, 0, "世界卫生日"), 188 new SolarHolidayStruct(4, 22, 0, "世界地球日"), 189 new SolarHolidayStruct(5, 1, 1, "劳动节"), 190 new SolarHolidayStruct(5, 2, 1, "劳动节假日"), 191 new SolarHolidayStruct(5, 3, 1, "劳动节假日"), 192 new SolarHolidayStruct(5, 4, 0, "青年节"), 193 new SolarHolidayStruct(5, 8, 0, "世界红十字日"), 194 new SolarHolidayStruct(5, 12, 0, "国际护士节"), 195 new SolarHolidayStruct(5, 31, 0, "世界无烟日"), 196 new SolarHolidayStruct(6, 1, 0, "国际儿童节"), 197 new SolarHolidayStruct(6, 5, 0, "世界环境保护日"), 198 new SolarHolidayStruct(6, 26, 0, "国际禁毒日"), 199 new SolarHolidayStruct(7, 1, 0, "建党节 香港回归纪念 世界建筑日"), 200 new SolarHolidayStruct(7, 11, 0, "世界人口日"), 201 new SolarHolidayStruct(8, 1, 0, "建军节"), 202 new SolarHolidayStruct(8, 8, 0, "中国男子节 父亲节"), 203 new SolarHolidayStruct(8, 15, 0, "抗日战争胜利纪念"), 204 new SolarHolidayStruct(9, 9, 0, "毛 泽 东逝世纪念"), 205 new SolarHolidayStruct(9, 10, 0, "教师节"), 206 new SolarHolidayStruct(9, 18, 0, "九·一八事变纪念日"), 207 new SolarHolidayStruct(9, 20, 0, "国际爱牙日"), 208 new SolarHolidayStruct(9, 27, 0, "世界旅游日"), 209 new SolarHolidayStruct(9, 28, 0, "孔子诞辰"), 210 new SolarHolidayStruct(10, 1, 1, "国庆节 国际音乐日"), 211 new SolarHolidayStruct(10, 2, 1, "国庆节假日"), 212 new SolarHolidayStruct(10, 3, 1, "国庆节假日"), 213 new SolarHolidayStruct(10, 6, 0, "老人节"), 214 new SolarHolidayStruct(10, 24, 0, "联合国日"), 215 new SolarHolidayStruct(11, 10, 0, "世界青年节"), 216 new SolarHolidayStruct(11, 12, 0, "孙中山诞辰纪念"), 217 new SolarHolidayStruct(12, 1, 0, "世界艾滋病日"), 218 new SolarHolidayStruct(12, 3, 0, "世界残疾人日"), 219 new SolarHolidayStruct(12, 20, 0, "澳门回归纪念"), 220 new SolarHolidayStruct(12, 24, 0, "平安夜"), 221 new SolarHolidayStruct(12, 25, 0, "圣诞节"), 222 new SolarHolidayStruct(12, 26, 0, "毛 泽 东诞辰纪念") 223 }; 224 225 #endregion 226 227 #region 按农历计算的节日 228 229 private static readonly LunarHolidayStruct[] lunarHolidayInfo = 230 { 231 new LunarHolidayStruct(1, 1, 1, "春节"), 232 new LunarHolidayStruct(1, 15, 0, "元宵节"), 233 new LunarHolidayStruct(5, 5, 0, "端午节"), 234 new LunarHolidayStruct(7, 7, 0, "七夕情人节"), 235 new LunarHolidayStruct(7, 15, 0, "中元节 盂兰盆节"), 236 new LunarHolidayStruct(8, 15, 0, "中秋节"), 237 new LunarHolidayStruct(9, 9, 0, "重阳节"), 238 new LunarHolidayStruct(12, 8, 0, "腊八节"), 239 new LunarHolidayStruct(12, 23, 0, "北方小年(扫房)"), 240 new LunarHolidayStruct(12, 24, 0, "南方小年(掸尘)") 241 //new LunarHolidayStruct(12, 30, 0, "除夕") //注意除夕需要其它方法进行计算 242 //除夕可能是廿九也可能是三十 243 }; 244 245 #endregion 246 247 #region 按某月第几个星期几 248 249 private static readonly WeekHolidayStruct[] weekHolidayInfo = 250 { 251 new WeekHolidayStruct(5, 2, 0, "母亲节"), 252 new WeekHolidayStruct(5, 3, 0, "全国助残日"), 253 new WeekHolidayStruct(6, 3, 0, "父亲节"), 254 new WeekHolidayStruct(9, 3, 2, "国际和平日"), 255 new WeekHolidayStruct(9, 4, 0, "国际聋人节"), 256 new WeekHolidayStruct(10, 1, 1, "国际住房日"), 257 new WeekHolidayStruct(10, 1, 3, "国际减轻自然灾害日"), 258 new WeekHolidayStruct(11, 4, 4, "感恩节") 259 }; 260 261 #endregion 262 263 #endregion 264 265 #region 构造函数 266 267 #region ChineseCalendar <公历日期初始化> 268 269 /// <summary> 270 /// 用一个标准的公历日期来初使化 271 /// </summary> 272 /// <param name="dt">公历日期</param> 273 public ChineseCalendar(DateTime dt) 274 { 275 //检查日期是否在限制范围内 276 if (dt < minSolarDate || dt > maxSolarDate) 277 { 278 throw new ChineseCalendarException("公历日期只支持在1901-2-19~2101-1-28范围内"); 279 } 280 _solarDate = dt; 281 LoadFromSolarDate(); 282 } 283 284 #endregion 285 286 #region ChineseCalendar <农历日期初始化> 287 288 /// <summary> 289 /// 用农历的日期来初使化 290 /// </summary> 291 /// <param name="year">农历年</param> 292 /// <param name="month">农历月</param> 293 /// <param name="day">农历日</param> 294 /// <param name="IsLeapMonth">是否闰月</param> 295 public ChineseCalendar(int year, int month, int day, bool IsLeapMonth) 296 { 297 _solarDate = GetDateFromLunarDate(year, month, day, IsLeapMonth); 298 LoadFromSolarDate(); 299 } 300 301 #endregion 302 303 #endregion 304 305 #region 私有函数 306 307 #region LoadFromSolarDate 308 309 /// <summary> 310 /// 利用ChineseLunisolarCalendar类初始化农历数据 311 /// </summary> 312 private void LoadFromSolarDate() 313 { 314 IsLunarLeapMonth = false; 315 316 LunarYear = calendar.GetYear(_solarDate); 317 LunarMonth = calendar.GetMonth(_solarDate); 318 LunarDay = calendar.GetDayOfMonth(_solarDate); 319 320 _sexagenary = null; 321 _animal = null; 322 _lunarYearString = null; 323 _lunarMonthString = null; 324 _lunarDayString = null; 325 326 int leapMonth = calendar.GetLeapMonth(LunarYear); 327 if (leapMonth != 0) 328 { 329 if (leapMonth == LunarMonth) 330 { 331 IsLunarLeapMonth = true; 332 LunarMonth -= 1; 333 } 334 else if (leapMonth > 0 && leapMonth < LunarMonth) 335 { 336 LunarMonth -= 1; 337 } 338 } 339 } 340 341 #endregion 342 343 #region GetDateFromLunarDate 344 345 /// <summary> 346 /// 阴历转阳历 347 /// </summary> 348 /// <param name="year">阴历年</param> 349 /// <param name="month">阴历月</param> 350 /// <param name="day">阴历日</param> 351 /// <param name="isLeapMonth">是否闰月</param> 352 private DateTime GetDateFromLunarDate(int year, int month, int day, bool isLeapMonth) 353 { 354 if (year < 1901 || year > 2100) 355 throw new ChineseCalendarException("只支持1901~2100期间的农历年"); 356 if (month < 1 || month > 12) 357 throw new ChineseCalendarException("表示月份的数字必须在1~12之间"); 358 359 int leapMonth = calendar.GetLeapMonth(year); 360 361 if (isLeapMonth && leapMonth != month + 1) 362 { 363 throw new ChineseCalendarException("该月不是闰月"); 364 } 365 366 if (isLeapMonth || (leapMonth > 0 && leapMonth <= month)) 367 { 368 if (day < 1 || day > calendar.GetDaysInMonth(year, month + 1)) 369 throw new ChineseCalendarException("农历日期输入有误"); 370 return calendar.ToDateTime(year, month + 1, day, 0, 0, 0, 0); 371 } 372 if (day < 1 || day > calendar.GetDaysInMonth(year, month)) 373 throw new ChineseCalendarException("农历日期输入有误"); 374 return calendar.ToDateTime(year, month, day, 0, 0, 0, 0); 375 } 376 377 #endregion 378 379 #region GetSexagenaryHour 380 381 /// <summary> 382 /// 获得指定时间的时辰 383 /// </summary> 384 /// <returns>时辰</returns> 385 /// 子:zi (晚上 11 时正至凌晨 1 时正) 鼠 鼠在这时间最活跃。 (一说为0:00~2:00,以此类推) 386 /// 丑:chou (凌晨 1 时正至凌晨 3 时正) 牛 牛在这时候吃完草,准备耕田。 387 /// 寅:yin (凌晨 3 时正至早上 5 时正) 虎 老虎在此时最猛。 388 /// 卯:mao (早上 5 时正至早上 7 时正 )兔 月亮又称玉兔,在这段时间还在天上。 389 /// 辰: chen (早上 7 时正至上午 9 时正) 龙 相传这是「群龙行雨」的时候 390 /// 巳:si (上午 9 时正至上午11时正) 蛇 在这时候隐蔽在草丛中 391 /// 午:wu (上午11时正至下午 1 时正) 马 这时候太阳最猛烈,相传这时阳气达到极限,阴气将会产生,而马是阴类动物。 392 /// 未:wei ( 下午 1 时正至下午 3 时正 ) 羊 羊在这段时间吃草 393 /// 申:shen ( 下午 3 时正至下午 5 时正) 猴 猴子喜欢在这时候啼叫 394 /// 酉:you (下午 5 时正至晚上 7 时正) 鸡 鸡於傍晚开始归巢 395 /// 戌:xu (晚上 7 时正至晚上 9 时正 ) 狗 狗开始守门口 396 /// 亥:hai (晚上 9 时正至晚上 11 时正) 猪 夜深时分猪正在熟睡 397 private string GetSexagenaryHour() 398 { 399 if (_solarDate.Hour == 23 || _solarDate.Hour == 0) 400 { 401 return TerrestrialBranch.Substring(0); 402 } 403 return TerrestrialBranch.Substring((_solarDate.Hour + 1)/2); 404 } 405 406 #endregion 407 408 #region ConvertToChineseNum 409 410 /// <summary> 411 /// 将0-9转成汉字形式 412 /// </summary> 413 /// <param name="n"></param> 414 /// <returns></returns> 415 private string ConvertToChineseNum(string n) 416 { 417 switch (n) 418 { 419 case "0": 420 return ChineseNumber.Substring(0, 1); 421 case "1": 422 return ChineseNumber.Substring(1, 1); 423 case "2": 424 return ChineseNumber.Substring(2, 1); 425 case "3": 426 return ChineseNumber.Substring(3, 1); 427 case "4": 428 return ChineseNumber.Substring(4, 1); 429 case "5": 430 return ChineseNumber.Substring(5, 1); 431 case "6": 432 return ChineseNumber.Substring(6, 1); 433 case "7": 434 return ChineseNumber.Substring(7, 1); 435 case "8": 436 return ChineseNumber.Substring(8, 1); 437 case "9": 438 return ChineseNumber.Substring(9, 1); 439 default: 440 return ""; 441 } 442 } 443 444 #endregion 445 446 #region CompareWeekDayHoliday 447 448 /// <summary> 449 /// 判断某个日期是不是指定的第几个月第几周第几天,星期天是第0天。 450 /// </summary> 451 /// <param name="date">指定的日期</param> 452 /// <param name="month">第几个月</param> 453 /// <param name="week">第几个星期</param> 454 /// <param name="day">第几天</param> 455 /// <returns>true或false</returns> 456 private bool CompareWeekDayHoliday(DateTime date, int month, int week, int day) 457 { 458 if (date.Month == month) //月份相同 459 { 460 if ((int) date.DayOfWeek == day) //星期几相同 461 { 462 var firstDay = new DateTime(date.Year, date.Month, 1); //生成当月第一天 463 int firWeekDays = 7 - (int) firstDay.DayOfWeek; //计算第一周有多少天 464 465 if (day + firWeekDays + (week - 2)*7 + 1 == date.Day) 466 { 467 return true; 468 } 469 } 470 } 471 472 return false; 473 } 474 475 #endregion 476 477 #endregion 478 479 #region 属性 480 481 #region 最小最大日期 482 483 /// <summary> 484 /// 所支持的最小农历年份 485 /// </summary> 486 public static int MinLunarYear 487 { 488 get { return 1901; } 489 } 490 491 /// <summary> 492 /// 所支持的最大农历年份 493 /// </summary> 494 public static int MaxLunarYear 495 { 496 get { return 2100; } 497 } 498 499 /// <summary> 500 /// 所支持的最小公历日期 501 /// </summary> 502 public static DateTime MinSolarDate 503 { 504 get { return new DateTime(1901, 2, 19); } 505 } 506 507 /// <summary> 508 /// 所支持的最大公历日期 509 /// </summary> 510 public static DateTime MaxSolarDate 511 { 512 get { return new DateTime(2101, 1, 28); } 513 } 514 515 #endregion 516 517 #region 节日 518 519 #region LunarDateHoliday 520 521 /// <summary> 522 /// 中国农历节日 523 /// </summary> 524 public string LunarDateHoliday 525 { 526 get 527 { 528 string tempStr = ""; 529 if (IsLunarLeapMonth == false) //闰月不计算节日 530 { 531 foreach (LunarHolidayStruct lh in lunarHolidayInfo) 532 { 533 if ((lh.Month == LunarMonth) && (lh.Day == LunarDay)) 534 { 535 tempStr = lh.HolidayName; 536 break; 537 } 538 } 539 540 //对除夕进行特别处理 541 if (LunarMonth == 12) 542 { 543 int leapMonth = calendar.GetLeapMonth(LunarYear); 544 int days; 545 //计算当年农历12月的总天数 546 if (leapMonth > 0 && LunarMonth > leapMonth - 1) 547 { 548 days = calendar.GetDaysInMonth(LunarYear, 13); 549 } 550 else 551 { 552 days = calendar.GetDaysInMonth(LunarYear, 12); 553 } 554 555 if (LunarDay == days) //如果为最后一天 556 { 557 tempStr = "除夕"; 558 } 559 } 560 } 561 return tempStr; 562 } 563 } 564 565 #endregion 566 567 #region WeekDayHoliday 568 569 /// <summary> 570 /// 按某月第几周第几日计算的节日 571 /// </summary> 572 public string WeekDayHoliday 573 { 574 get 575 { 576 string tempStr = ""; 577 foreach (WeekHolidayStruct wh in weekHolidayInfo) 578 { 579 if (CompareWeekDayHoliday(_solarDate, wh.Month, wh.WeekAtMonth, wh.WeekDay)) 580 { 581 tempStr = wh.HolidayName; 582 break; 583 } 584 } 585 return tempStr; 586 } 587 } 588 589 #endregion 590 591 #region SolarDateHoliday 592 593 /// <summary> 594 /// 按公历日计算的节日 595 /// </summary> 596 public string SolarDateHoliday 597 { 598 get 599 { 600 string tempStr = ""; 601 602 foreach (SolarHolidayStruct sh in solarHolidayInfo) 603 { 604 if ((sh.Month == _solarDate.Month) && (sh.Day == _solarDate.Day)) 605 { 606 tempStr = sh.HolidayName; 607 break; 608 } 609 } 610 return tempStr; 611 } 612 } 613 614 #endregion 615 616 #endregion 617 618 #region 公历日期 619 620 #region SolarDate 621 622 /// <summary> 623 /// 取对应的公历日期 624 /// </summary> 625 public DateTime SolarDate 626 { 627 get { return _solarDate; } 628 set 629 { 630 if (_solarDate.Equals(value)) 631 { 632 } 633 else 634 { 635 //检查日期是否在限制范围内 636 if (value < minSolarDate || value > maxSolarDate) 637 { 638 throw new ChineseCalendarException("公历日期只支持在1901-2-19~2101-1-28范围内"); 639 } 640 _solarDate = value; 641 LoadFromSolarDate(); 642 } 643 } 644 } 645 646 #endregion 647 648 #region WeekDay 649 650 /// <summary> 651 /// 星期几的英文表示 652 /// </summary> 653 public DayOfWeek WeekDay 654 { 655 get { return _solarDate.DayOfWeek; } 656 } 657 658 #endregion 659 660 #region WeekDayString 661 662 /// <summary> 663 /// 星期几的中文表示 664 /// </summary> 665 public string WeekDayString 666 { 667 get 668 { 669 switch (_solarDate.DayOfWeek) 670 { 671 case DayOfWeek.Sunday: 672 return "星期日"; 673 case DayOfWeek.Monday: 674 return "星期一"; 675 case DayOfWeek.Tuesday: 676 return "星期二"; 677 case DayOfWeek.Wednesday: 678 return "星期三"; 679 case DayOfWeek.Thursday: 680 return "星期四"; 681 case DayOfWeek.Friday: 682 return "星期五"; 683 default: 684 return "星期六"; 685 } 686 } 687 } 688 689 #endregion 690 691 #region DateString 692 693 /// <summary> 694 /// 公历日期中文表示法 如一九九七年七月一日 695 /// </summary> 696 public string DateString 697 { 698 get 699 { 700 string str = ""; 701 int i; 702 string num = _solarDate.Year.ToString(); 703 for (i = 0; i < num.Length; i++) 704 { 705 str += ConvertToChineseNum(num.Substring(i, 1)); 706 } 707 str += "年"; 708 num = _solarDate.Month.ToString(); 709 for (i = 0; i < num.Length; i++) 710 { 711 str += ConvertToChineseNum(num.Substring(i, 1)); 712 } 713 str += "月"; 714 num = _solarDate.Day.ToString(); 715 for (i = 0; i < num.Length; i++) 716 { 717 str += ConvertToChineseNum(num.Substring(i, 1)); 718 } 719 str += "日"; 720 return str; 721 } 722 } 723 724 #endregion 725 726 #region ChineseConstellation 727 728 /// <summary> 729 /// 28星宿计算 730 /// </summary> 731 public string ChineseConstellation 732 { 733 get 734 { 735 TimeSpan ts = _solarDate - ChineseConstellationReferDay; 736 int offset = ts.Days; 737 int modStarDay = offset%28; 738 return (modStarDay >= 0 739 ? ChineseConstellationName[modStarDay] 740 : ChineseConstellationName[27 + modStarDay]); 741 } 742 } 743 744 #endregion 745 746 #endregion 747 748 #region 农历日期 749 750 #region IsLunarLeapMonth 751 752 /// <summary> 753 /// 是否闰月 754 /// </summary> 755 public bool IsLunarLeapMonth { get; private set; } 756 757 #endregion 758 759 #region LunarDay 760 761 /// <summary> 762 /// 农历日 763 /// </summary> 764 public int LunarDay { get; private set; } 765 766 #endregion 767 768 #region LunarDayString 769 770 /// <summary> 771 /// 农历日的中文表示 772 /// </summary> 773 public string LunarDayString 774 { 775 get 776 { 777 if (string.IsNullOrEmpty(_lunarDayString)) 778 _lunarDayString = ChineseDayName[LunarDay - 1]; 779 return _lunarDayString; 780 } 781 } 782 783 #endregion 784 785 #region LunarMonth 786 787 /// <summary> 788 /// 农历的月份 789 /// </summary> 790 public int LunarMonth { get; private set; } 791 792 #endregion 793 794 #region LunarMonthString 795 796 /// <summary> 797 /// 农历月份中文表示 798 /// </summary> 799 public string LunarMonthString 800 { 801 get 802 { 803 if (string.IsNullOrEmpty(_lunarMonthString)) 804 _lunarMonthString = ChineseMonthName[LunarMonth - 1]; 805 return _lunarMonthString; 806 } 807 } 808 809 #endregion 810 811 #region LunarYear 812 813 /// <summary> 814 /// 农历年份 815 /// </summary> 816 public int LunarYear { get; private set; } 817 818 #endregion 819 820 #region LunarYearString 821 822 /// <summary> 823 /// 农历年的中文表示 824 /// </summary> 825 public string LunarYearString 826 { 827 get 828 { 829 if (string.IsNullOrEmpty(_lunarYearString)) 830 { 831 _lunarYearString = ""; 832 string num = LunarYear.ToString(); 833 for (int i = 0; i < 4; i++) 834 { 835 _lunarYearString += ChineseNumber.Substring(Convert.ToInt32(num.Substring(i, 1)), 1); 836 } 837 } 838 return _lunarYearString; 839 } 840 } 841 842 #endregion 843 844 #endregion 845 846 #region 24节气 847 848 /// <summary> 849 /// 定气法计算二十四节气 850 /// </summary> 851 /// <remarks> 852 /// 节气的定法有两种。古代历法采用的称为"恒气",即按时间把一年等分为24份, 853 /// 每一节气平均得15天有余,所以又称"平气"。现代农历采用的称为"定气",即 854 /// 按地球在轨道上的位置为标准,一周360°,两节气之间相隔15°。由于冬至时地 855 /// 球位于近日点附近,运动速度较快,因而太阳在黄道上移动15°的时间不到15天。 856 /// 夏至前后的情况正好相反,太阳在黄道上移动较慢,一个节气达16天之多。采用 857 /// 定气时可以保证春、秋两分必然在昼夜平分的那两天。 858 /// 立春:立是开始的意思,立春就是春季的开始。 859 /// 雨水:降雨开始,雨量渐增。 860 /// 惊蛰:蛰是藏的意思。惊蛰是指春雷乍动,惊醒了蛰伏在土中冬眠的动物。 861 /// 春分:分是平分的意思。春分表示昼夜平分。 862 /// 清明:天气晴朗,草木繁茂。 863 /// 谷雨:雨生百谷。雨量充足而及时,谷类作物能茁壮成长。 864 /// 立夏:夏季的开始。 865 /// 小满:麦类等夏熟作物籽粒开始饱满。 866 /// 芒种:麦类等有芒作物成熟。 867 /// 夏至:炎热的夏天来临。 868 /// 小暑:暑是炎热的意思。小暑就是气候开始炎热。 869 /// 大署:一年中最热的时候。 870 /// 立秋:秋季的开始。 871 /// 处暑:处是终止、躲藏的意思。处暑是表示炎热的暑天结束。 872 /// 白露:天气转凉,露凝而白。 873 /// 秋分:昼夜平分。 874 /// 寒露:露水以寒,将要结冰。 875 /// 霜降:天气渐冷,开始有霜。 876 /// 立冬:冬季的开始。 877 /// 小雪:开始下雪。 878 /// 大雪:降雪量增多,地面可能积雪。 879 /// 冬至:寒冷的冬天来临。 880 /// 小寒:气候开始寒冷。 881 /// 大寒:一年中最冷的时候。 882 /// </remarks> 883 public string TwentyFourSolarTerms 884 { 885 get 886 { 887 var baseDateAndTime = new DateTime(1900, 1, 6, 2, 5, 0); //#1/6/1900 2:05:00 AM# 888 string tempStr = ""; 889 890 int y = _solarDate.Year; 891 892 for (int i = 0; i < 24; i++) 893 { 894 double num = 525948.76*(y - 1900) + sTermsInfo[i]; 895 896 DateTime newDate = baseDateAndTime.AddMinutes(num); 897 if (newDate.DayOfYear == _solarDate.DayOfYear) 898 { 899 tempStr = SolarTerms[i]; 900 break; 901 } 902 } 903 return tempStr; 904 } 905 } 906 907 /// <summary> 908 /// 当前日期前一个最近节气的日期 909 /// </summary> 910 public DateTime PrevSolarTermsDate 911 { 912 get { return GetPrevSolarTermsDate(_solarDate); } 913 } 914 915 /// <summary> 916 /// 当前日期后一个最近节气的日期 917 /// </summary> 918 public DateTime NextSolarTremsDate 919 { 920 get { return GetNextSolarTremsDate(_solarDate); } 921 } 922 923 #endregion 924 925 #region 星座和诞生石 926 927 #region Constellation 928 929 /// <summary> 930 /// 星座 931 /// </summary> 932 public string Constellation 933 { 934 get { return GetConstellation(_solarDate); } 935 } 936 937 #endregion 938 939 #region BirthStone 940 941 /// <summary> 942 /// 诞生石 943 /// </summary> 944 public string BirthStone 945 { 946 get { return GetBirthStone(_solarDate); } 947 } 948 949 #endregion 950 951 #endregion 952 953 #region 属相 954 955 /// <summary> 956 /// 属相 957 /// </summary> 958 public string AnimalString 959 { 960 get 961 { 962 if (string.IsNullOrEmpty(_animal)) 963 { 964 int y = calendar.GetSexagenaryYear(_solarDate); 965 _animal = AnimalsStr.Substring((y - 1)%12, 1); 966 } 967 968 return _animal; 969 } 970 } 971 972 #endregion 973 974 #region 天干地支 975 976 #region SexagenaryYear 977 978 /// <summary> 979 /// 农历年的干支表示,如乙丑年。 980 /// </summary> 981 public string SexagenaryYear 982 { 983 get 984 { 985 if (string.IsNullOrEmpty(_sexagenary)) 986 { 987 int y = calendar.GetSexagenaryYear(_solarDate); 988 _sexagenary = CelestialStem.Substring((y - 1)%10, 1) + 989 TerrestrialBranch.Substring((y - 1)%12, 1); 990 } 991 return _sexagenary; 992 } 993 } 994 995 #endregion 996 997 #region SexagenaryMonth 998 999 /// <summary> 1000 /// 干支的月表示,注意农历的闰月不记干支 1001 /// </summary> 1002 public string SexagenaryMonth 1003 { 1004 get 1005 { 1006 //每个月的地支总是固定的,而且总是从寅月开始 1007 int zhiIndex; 1008 if (LunarMonth > 10) 1009 { 1010 zhiIndex = LunarMonth - 10; 1011 } 1012 else 1013 { 1014 zhiIndex = LunarMonth + 2; 1015 } 1016 string zhi = TerrestrialBranch.Substring(zhiIndex - 1, 1); 1017 1018 //根据当年的干支年的干来计算月干的第一个 1019 int ganIndex = 1; 1020 int i = (LunarYear - GanZhiStartYear)%60; //计算干支 1021 switch (i%10) 1022 { 1023 #region ... 1024 1025 case 0: //甲 1026 ganIndex = 3; 1027 break; 1028 case 1: //乙 1029 ganIndex = 5; 1030 break; 1031 case 2: //丙 1032 ganIndex = 7; 1033 break; 1034 case 3: //丁 1035 ganIndex = 9; 1036 break; 1037 case 4: //戊 1038 ganIndex = 1; 1039 break; 1040 case 5: //己 1041 ganIndex = 3; 1042 break; 1043 case 6: //庚 1044 ganIndex = 5; 1045 break; 1046 case 7: //辛 1047 ganIndex = 7; 1048 break; 1049 case 8: //壬 1050 ganIndex = 9; 1051 break; 1052 case 9: //癸 1053 ganIndex = 1; 1054 break; 1055 1056 #endregion 1057 } 1058 string gan = CelestialStem.Substring((ganIndex + LunarMonth - 2)%10, 1); 1059 1060 return gan + zhi; 1061 } 1062 } 1063 1064 #endregion 1065 1066 #region SexagenaryDay 1067 1068 /// <summary> 1069 /// 取干支日表示法 1070 /// </summary> 1071 public string SexagenaryDay 1072 { 1073 get 1074 { 1075 TimeSpan ts = _solarDate - GanZhiStartDay; 1076 int offset = ts.Days; 1077 int i = offset%60; 1078 return CelestialStem.Substring(i%10, 1) + TerrestrialBranch.Substring(i%12, 1); 1079 } 1080 } 1081 1082 #endregion 1083 1084 #region SexagenaryHour 1085 1086 /// <summary> 1087 /// 时辰 1088 /// </summary> 1089 public string SexagenaryHour 1090 { 1091 get 1092 { 1093 _solarDate = new DateTime(_solarDate.Year, _solarDate.Month, _solarDate.Day, 1094 DateTime.Today.Hour, DateTime.Today.Minute, DateTime.Today.Second); 1095 return GetSexagenaryHour(); 1096 } 1097 } 1098 1099 #endregion 1100 1101 #region SexagenaryDateString 1102 1103 /// <summary> 1104 /// 取当前日期的干支表示法如 甲子年乙丑月丙庚日 1105 /// </summary> 1106 public string SexagenaryDateString 1107 { 1108 get { return SexagenaryYear + "年" + SexagenaryMonth + "月" + SexagenaryDay + "日"; } 1109 } 1110 1111 #endregion 1112 1113 #endregion 1114 1115 #endregion 1116 1117 #region 公共方法 1118 1119 #region GetPrevSolarTermsDate 1120 1121 /// <summary> 1122 /// 获取指定日期前一个最近节气的日期 1123 /// </summary> 1124 public DateTime GetPrevSolarTermsDate(DateTime dt) 1125 { 1126 var baseDateAndTime = new DateTime(1900, 1, 6, 2, 5, 0); //#1/6/1900 2:05:00 AM# 1127 var newDate = new DateTime(); 1128 1129 int y = dt.Year; 1130 1131 for (int i = 24; i >= 1; i--) 1132 { 1133 double num = 525948.76*(y - 1900) + sTermsInfo[i - 1]; 1134 1135 newDate = baseDateAndTime.AddMinutes(num); //按分钟计算 1136 1137 if (newDate.DayOfYear < dt.DayOfYear) 1138 { 1139 break; 1140 } 1141 } 1142 return newDate; 1143 } 1144 1145 #endregion 1146 1147 #region GetNextSolarTremsDate 1148 1149 /// <summary> 1150 /// 获取指定日期后一个最近节气的日期 1151 /// </summary> 1152 public DateTime GetNextSolarTremsDate(DateTime dt) 1153 { 1154 var baseDateAndTime = new DateTime(1900, 1, 6, 2, 5, 0); //#1/6/1900 2:05:00 AM# 1155 var newDate = new DateTime(); 1156 1157 int y = dt.Year; 1158 1159 for (int i = 1; i <= 24; i++) 1160 { 1161 double num = 525948.76*(y - 1900) + sTermsInfo[i - 1]; 1162 1163 newDate = baseDateAndTime.AddMinutes(num); //按分钟计算 1164 1165 if (newDate.DayOfYear > dt.DayOfYear) 1166 { 1167 break; 1168 } 1169 } 1170 return newDate; 1171 } 1172 1173 #endregion 1174 1175 #region GetConstellation 1176 1177 /// <summary> 1178 /// 获取指定公历日期的星座 1179 /// </summary> 1180 /// <param name="dt">指定的日期</param> 1181 /// <returns>星座的字符串</returns> 1182 public string GetConstellation(DateTime dt) 1183 { 1184 int index; 1185 int m = dt.Month; 1186 int d = dt.Day; 1187 int y = m*100 + d; 1188 1189 if (((y >= 321) && (y <= 420))) 1190 { 1191 index = 0; 1192 } 1193 else if ((y >= 421) && (y <= 520)) 1194 { 1195 index = 1; 1196 } 1197 else if ((y >= 521) && (y <= 620)) 1198 { 1199 index = 2; 1200 } 1201 else if ((y >= 621) && (y <= 722)) 1202 { 1203 index = 3; 1204 } 1205 else if ((y >= 723) && (y <= 822)) 1206 { 1207 index = 4; 1208 } 1209 else if ((y >= 823) && (y <= 922)) 1210 { 1211 index = 5; 1212 } 1213 else if ((y >= 923) && (y <= 1022)) 1214 { 1215 index = 6; 1216 } 1217 else if ((y >= 1023) && (y <= 1121)) 1218 { 1219 index = 7; 1220 } 1221 else if ((y >= 1122) && (y <= 1221)) 1222 { 1223 index = 8; 1224 } 1225 else if ((y >= 1222) || (y <= 119)) 1226 { 1227 index = 9; 1228 } 1229 else if ((y >= 120) && (y <= 218)) 1230 { 1231 index = 10; 1232 } 1233 else if ((y >= 219) && (y <= 320)) 1234 { 1235 index = 11; 1236 } 1237 else 1238 { 1239 index = 0; 1240 } 1241 1242 return ConstellationName[index]; 1243 } 1244 1245 #endregion 1246 1247 #region GetBirthStone 1248 1249 /// <summary> 1250 /// 获取指定公历日期的诞生石 1251 /// </summary> 1252 /// <param name="dt">指定的日期</param> 1253 /// <returns>诞生石字符串</returns> 1254 public string GetBirthStone(DateTime dt) 1255 { 1256 int index; 1257 int m = dt.Month; 1258 int d = dt.Day; 1259 int y = m*100 + d; 1260 1261 if (((y >= 321) && (y <= 420))) 1262 { 1263 index = 0; 1264 } 1265 else if ((y >= 421) && (y <= 520)) 1266 { 1267 index = 1; 1268 } 1269 else if ((y >= 521) && (y <= 620)) 1270 { 1271 index = 2; 1272 } 1273 else if ((y >= 621) && (y <= 722)) 1274 { 1275 index = 3; 1276 } 1277 else if ((y >= 723) && (y <= 822)) 1278 { 1279 index = 4; 1280 } 1281 else if ((y >= 823) && (y <= 922)) 1282 { 1283 index = 5; 1284 } 1285 else if ((y >= 923) && (y <= 1022)) 1286 { 1287 index = 6; 1288 } 1289 else if ((y >= 1023) && (y <= 1121)) 1290 { 1291 index = 7; 1292 } 1293 else if ((y >= 1122) && (y <= 1221)) 1294 { 1295 index = 8; 1296 } 1297 else if ((y >= 1222) || (y <= 119)) 1298 { 1299 index = 9; 1300 } 1301 else if ((y >= 120) && (y <= 218)) 1302 { 1303 index = 10; 1304 } 1305 else if ((y >= 219) && (y <= 320)) 1306 { 1307 index = 11; 1308 } 1309 else 1310 { 1311 index = 0; 1312 } 1313 1314 return BirthStoneName[index]; 1315 } 1316 1317 #endregion 1318 1319 #region ToString 1320 1321 /// <summary> 1322 /// 农历日期的字符串显示,如:二〇〇九年十二月廿四 1323 /// </summary> 1324 /// <returns>农历日期的字符串显示</returns> 1325 public override string ToString() 1326 { 1327 if (IsLunarLeapMonth == false) 1328 { 1329 return LunarYearString + "年" + LunarMonthString + 1330 "月" + LunarDayString; 1331 } 1332 return LunarYearString + "年闰" + LunarMonthString + 1333 "月" + LunarDayString; 1334 } 1335 1336 #endregion 1337 1338 #region ToSexagenaryString 1339 1340 /// <summary> 1341 /// 农历日期天干地支年加生肖表示法,如:己丑(牛)年腊月廿四 1342 /// </summary> 1343 /// <returns>农历日期天干地支年加生肖表示法</returns> 1344 public string ToSexAnimalString() 1345 { 1346 if (IsLunarLeapMonth == false) 1347 { 1348 return SexagenaryYear + "(" + AnimalString + ")年" + 1349 LunarMonthString + "月" + LunarDayString; 1350 } 1351 return SexagenaryYear + "(" + AnimalString + ")年闰" + 1352 LunarMonthString + "月" + LunarDayString; 1353 } 1354 1355 #endregion 1356 1357 #region GetLeapMonth 1358 1359 /// <summary> 1360 /// 获取闰月的月份,如0表示无闰月,6表示闰5月。 1361 /// </summary> 1362 /// <param name="year">农历年份</param> 1363 /// <returns>闰月是第几个月</returns> 1364 public static int GetLeapMonth(int year) 1365 { 1366 if (year < 1901 || year > 2100) 1367 throw new ChineseCalendarException("只支持1901~2100期间的农历年"); 1368 return calendar.GetLeapMonth(year); 1369 } 1370 1371 #endregion 1372 1373 #region GetDaysInMonth 1374 1375 /// <summary> 1376 /// 计算该农历年份月份的天数 1377 /// </summary> 1378 /// <param name="year">农历年</param> 1379 /// <param name="month">农历月,如果没有闰月是(1~12),有闰月是(1~13)</param> 1380 /// <returns>天数</returns> 1381 public static int GetDaysInMonth(int year, int month) 1382 { 1383 if (year < 1901 || year > 2100) 1384 throw new ChineseCalendarException("只支持1901~2100期间的农历年"); 1385 return calendar.GetDaysInMonth(year, month); 1386 } 1387 1388 #endregion 1389 1390 #region AddDays 1391 1392 /// <summary> 1393 /// 将指定的天数加到此农历日期上 1394 /// </summary> 1395 /// <param name="n">天数</param> 1396 /// <returns>农历日期</returns> 1397 public ChineseCalendar AddDays(int n) 1398 { 1399 return new ChineseCalendar(SolarDate.AddDays(n)); 1400 } 1401 1402 #endregion 1403 1404 #region AddMonths 1405 1406 /// <summary> 1407 /// 将指定的月份数加到此农历日期上 1408 /// </summary> 1409 /// <param name="n">月份数</param> 1410 /// <returns>农历日期</returns> 1411 public ChineseCalendar AddMonths(int n) 1412 { 1413 var temp = new ChineseCalendar(_solarDate); 1414 for (int i = 0; i < n; i++) 1415 { 1416 temp.SolarDate = temp._solarDate.AddDays(29); 1417 if (temp.LunarMonth == LunarMonth && 1418 (IsLunarLeapMonth || (IsLunarLeapMonth == false && 1419 temp.IsLunarLeapMonth == false))) 1420 { 1421 temp.LunarMonth++; 1422 temp._lunarMonthString = null; 1423 } 1424 1425 int days = GetDaysInMonth(temp.LunarYear, temp.LunarMonth); 1426 temp.LunarDay = days < LunarDay ? days : LunarDay; 1427 1428 temp._lunarDayString = null; 1429 } 1430 1431 return temp; 1432 } 1433 1434 #endregion 1435 1436 #region AddYears 1437 1438 /// <summary> 1439 /// 将指定的年份数加到此农历日期上 1440 /// </summary> 1441 /// <param name="n">年份</param> 1442 /// <returns>农历日期</returns> 1443 public ChineseCalendar AddYears(int n) 1444 { 1445 ChineseCalendar temp = this; 1446 temp.LunarYear += n; 1447 temp.IsLunarLeapMonth = false; 1448 temp._lunarYearString = null; 1449 temp._sexagenary = null; 1450 temp._animal = null; 1451 int days = GetDaysInMonth(temp.LunarYear, temp.LunarMonth); 1452 if (days < LunarDay) 1453 { 1454 temp.LunarDay = days; 1455 temp._lunarDayString = null; 1456 } 1457 1458 return temp; 1459 } 1460 1461 #endregion 1462 1463 #endregion 1464 }