文章提供计算农历天干地支及当年属相的算法源程序,使用的语言为Javascript、VBScript、C#等。
一、C# 代码(1):
原来还准备自己写算法,并研究农历规则。发现那太难和麻烦了,光是农历的推算那就我等专门研究历法的人一下搞懂的。后来发现
。NET类库也提供一些基础的农历类System.Globalization.ChineseLunisolarCalendar。我改装了一下如DateTime时间形式。代码如
下。实现了 公历农历转换的功能。但是只能算到1900~2100年之间的。基本够日常使用了。源代码如下:
1.using System;
2.using System.Collections.Generic;
3.using System.Text;
4.5.namespace System
6.{
7. 8. /// 中国常用农历日期时间类 9. /// [email protected] http://hi.csdn.net/zj53hao 10.
/// 11. class ChinaDateTime
12. {
13. private int year, month, dayOfMonth;
14. private bool isLeap;
15. public DateTime time;
16.17. 18. /// 获取当前日期的农历年 19. /// 20. public int
Year
21. {
22. get { return year; }
23. }
24.25. 26. /// 获取当前日期的农历月份 27. /// 28. public int
Month
29. {
30. get { return month; }
31. }
32.33. 34. /// 获取当前日期的农历月中天数 35. /// 36. public
int DayOfMonth
37. {
38. get { return dayOfMonth; }
39. }
40.41. 42. /// 获取当前日期是否为闰月中的日期 43. /// 44.
public bool IsLeap
45. {
46. get { return isLeap; }
47. }
48.49. System.Globalization.ChineseLunisolarCalendar cc;
50. 51. /// 返回指定公历日期的阴历时间 52. /// 53. ///
name="time"> 54. public ChinaDateTime(DateTime time)
55. {
56. cc = new System.Globalization.ChineseLunisolarCalendar();
57.
58. if (time > cc.MaxSupportedDateTime || time < cc.MinSupportedDateTime)
59. throw new Exception("参数日期时间不在支持的范围内,支持范围:" +
cc.MinSupportedDateTime.ToShortDateString()+"到"+cc.MaxSupportedDateTime.ToShortDateString());
60. year = cc.GetYear(time);
61. month = cc.GetMonth(time);
62. dayOfMonth = cc.GetDayOfMonth(time);
63. isLeap = cc.IsLeapMonth(year, month);
64. if (isLeap) month -= 1;
65. this.time = time;
66.67. }
68.69. 70. /// 返回当前日前的农历日期。 71. /// 72. public
static ChinaDateTime Now
73. {
74. get { return new ChinaDateTime(DateTime.Now); }
75. }
76.77. 78. /// 返回指定农历年,月,日,是否为闰月的农历日期时间 79. ///
80. /// 81. /// 82. ///
83. /// 84. public ChinaDateTime(int
Year, int Month, int DayOfMonth, bool IsLeap)
85. {
86. if (Year >= cc.MaxSupportedDateTime.Year || Year <= cc.MinSupportedDateTime.Year)
87. throw new Exception("参数年份时间不在支持的范围内,支持范围:" +
cc.MinSupportedDateTime.ToShortDateString() + "到" + cc.MaxSupportedDateTime.ToShortDateString());
88.89. if (Month < 1 || Month > 12)
90. throw new Exception("月份必须在1~12范围");
91. cc = new System.Globalization.ChineseLunisolarCalendar();
92.
93. if(cc.GetLeapMonth(Year)!=Month&&IsLeap)
94. throw new Exception("指定的月份不是当年的闰月");
95. if (cc.GetDaysInMonth(Year, IsLeap ? Month + 1 : Month) < DayOfMonth || DayOfMonth < 1)
96. throw new Exception("指定的月中的天数不在当前月天数有效范围");
97. year = Year;
98. month = Month;
99. dayOfMonth = DayOfMonth;
100. isLeap = IsLeap;
101. time = DateTime.Now;
102. }
103.104. 105. /// 获取当前农历日期的公历时间 106. /// 107.
public DateTime ToDateTime()
108. {
109. return cc.ToDateTime(year, isLeap ? month + 1 : month, dayOfMonth, time.Hour, time.Minute,
time.Second, time.Millisecond);
110. }
111.112. 113. /// 获取指定农历时间对应的公历时间 114. /// 115.
/// 116. /// 117. public static DateTime ToDateTime
(ChinaDateTime CnTime)
118. {
119. return CnTime.ToDateTime();
120. }
121.122. 123. /// 获取指定公历时间转换为农历时间 124. /// 125.
/// 126. /// 127. public static ChinaDateTime
ToChinaDateTime(DateTime Time)
128. {
129. return new ChinaDateTime(Time);
130. }
131. }
132.}
二、C#代码(2):
1、农历类的使用
.net框架不支持直接将日期转换成农历格式的字符串,那么要将显示农历格式的日期,就只要自已写代码了。不过由于已经有了
ChineseLunisolarCalendar类实现了公历转换为农历日期的功能,所以要写这样的代码也比较简单。需要用到
ChineseLunisolarCalendar以下几个主要方法:
int GetYear (DateTime time) 获取指定公历日期的农历年份,使用的还是公历纪元。在每年的元旦之后春节之前农历的纪年会比公
历小1,其它时候等于公历纪年。虽然农历使用传说中的耶稣生日纪元似乎不太妥当,不过我们确实已经几十年没有实行一个更好的纪
年办法,也只有将就了。
int GetMonth (DateTime time) 获取指定公历日期的农历月份。这里要注意了,由于农历有接近三分之一的年份存在闰月,则在这
些年份里会有十三个,而具体哪一个月是闰月也说不准,这里不同于希伯来历。以今年为例,今年闰七月,则此方法在参数为闰七月
的日期是返回值为 8,参数为农历十二月的日期时返回值为13
bool IsLeapMonth ( int year, int month) 获取指定农历年份和月份是否为闰月,这个函数和上个函数配合使用就可以算出农历
的月份了。
int GetDayOfMonth (DateTime time) 获取指定公历日期的农历天数,这个值根据大月或者小月取值是1到30或者1到29, MSDN上说的
1到31显然是错的, 没有哪个农历月份会有31天。
int GetSexagenaryYear (DateTime time) 获取指定公历日期的农历年份的干支纪年,从1到60,分别是甲子、乙丑、丙寅、….癸亥
, 比如戊戌变法、辛亥革命就是按这个来命名的。当然算八字也少不了这个。
int GetCelestialStem (int sexagenaryYear) 获取一个天支的天干, 从1到10, 表示甲、乙、丙….,说白了就是对10取模。
int GetTerrestrialBranch (int sexagenaryYear) ) 获取一个干支的地支,, 从1到12, 表示子、丑、寅、…今年是狗年,那么今
年年份的地支就是“戌”。
有了这几个方法,显示某天的农历月份日期、农历节日等都是小菜一碟,算命先生排八字用这几个方法,又快又准确,写出的代码也
很短。
2、几种东亚农历类的区别
经过我的测试,ChineseLunisolarCalendar, JapaneseLunisolarCalendar, KoreanLunisolaCalendarr, TaiwanLunisolarCalendar
这四种日历,无论哪一种,以2006年2月6日为参数,调用它们的GetMonth方法得到的结果都是1,GetDayOfMonth得到的结果都是8。
想想也是,我们过的端午节和韩国的不太可能不是一天。
但是调用GetYear方法得到结果就有区别了ChineseLunisolarCalendar和KoreanLunisolarCalendar都返回2006,也就是公历纪年,
TaiwanLunisolarCalendar的返回值是95,依然是民国纪年,JapaneseLunisolarCalendar的返回值是18, 平成纪年。
另外的一个区别是这四种日历的MinSupportedDateTime和MaxSupportedDateTime各不一样,以下是对照表:
日历类 MinSupportedDateTime MaxSupportedDateTime
ChineseLunisolarCalendar 公元1901年1月初1 公元2100年12月29
TaiwanLunisolarCalendar 民国1年1月初1 民国139年12月29
JapaneseLunisolarCalendar 昭和35年1月初1 平成61年12月29
KoreanLunisolarCalendar 公元918年1月初1 公元2050年12月29
韩国农历类支持的最小日期为918年(也即高丽王朝建立的年份),以此而论,中国农历类支持的最小日期不说从商周算起,从汉唐算
总该没问题吧?微软公司啊,又在“厚彼薄此”,唉。
其次,日本还以天皇纪年,如果哪天xxxx, 岂不是使用JapaneseLunisolarCalendar写出的程序都有问题啦?
3、写自已的日期格式化器
昨天看了一篇文章,说目前大家用的“农历”这个术语是文革时期才有的,目的是反封建。这里为了省事,还是继续使用这个术语。
而英文名称ChineseLunisolarCalendar太长,我自己的代码中就用ChineseCalendar为相关功能命名,这个名字也还过得去吧。
我原先设想自定义一个类,使得能写出这样的代码:
string s= DateTime.Now.ToString(new MyFormatProvider());
就能得出我想要的农历日期字符串,经过测试却失败了,依据我的分析,微软公司在.net框架中把日期时间型的格式写死了,只能依
据相关的地区采用固定的几种显示格式,没法再自行定义。而前文已经说过,而所有的相关格式微软公司都放到一个名为
culture.nlp的文件中(这个文件在以前的.net框架是一个独立的文件,在.net 2.0被作为一个资源编译到mscorlib.dll中。) (我的
这个不能为DateTime写自已的格式化器的观点没有资料佐证,如有不当之处,请大家指正)
虽然不能为DataTime写自定义的格式器,但还有另外一个途径,就是为String类的Format方法写自定义格式化器,我测试了一下,效
果还不错,调用方式如下:
string s= String.Format(new ChineseCalendarFormatter(), "{0:D}",DateTime.Now);
可以得到“二〇〇六年正月初九”
string s= String.Format(new ChineseCalendarFormatter(), "{0:d}",DateTime.Now);
可以得到“丙戌年正月初九”
虽然没有前面所设想的方便,但也还能接受,全部代码帖出如下:
第一个类,主要是封装了农历的一些常用字符和对日历处理的最基本功能
1.using System;
2.using System.Collections.Generic;
3.using System.Text;
4.5.using System.Globalization;
6.7.public static class ChineseCalendarHelper
8.{
9. public static string GetYear(DateTime time)
10. {
11. StringBuilder sb = new StringBuilder();
12. int year = calendar.GetYear(time);
13. int d;
14. do15. {
16. d = year % 10;
17. sb.Insert(0, ChineseNumber[d]);
18. year = year / 10;
19. } while (year > 0);
20. return sb.ToString();
21. }
22.23. public static string GetMonth(DateTime time)
24. {
25. int month = calendar.GetMonth(time);
26. int year = calendar.GetYear(time);
27. int leap = 0;
28.29. //正月不可能闰月 30. for (int i = 3; i <= month; i++)
31. {
32. if (calendar.IsLeapMonth(year, i))
33. {
34. leap = i;
35. break; //一年中最多有一个闰月 36. }
37.38. }
39. if (leap > 0) month--;
40. return (leap == month + 1 ? "闰" : "") + ChineseMonthName[month - 1];
41. }
42.43. public static string GetDay(DateTime time)
44. {
45. return ChineseDayName[calendar.GetDayOfMonth(time) - 1];
46. }
47.48. public static string GetStemBranch(DateTime time)
49. {
50. int sexagenaryYear = calendar.GetSexagenaryYear(time);
51. string stemBranch = CelestialStem.Substring(sexagenaryYear % 10 - 1, 1) +
52. TerrestrialBranch.Substring(sexagenaryYear % 12 - 1, 1);
53. return stemBranch;
54. }
55.56. private static ChineseLunisolarCalendar calendar = new ChineseLunisolarCalendar();
57. private static string ChineseNumber = "〇一二三四五六七八九";
58. public const string CelestialStem = "甲乙丙丁戊己庚辛壬癸";
59. public const string TerrestrialBranch = "子丑寅卯辰巳午未申酉戌亥";
60. public static readonly string[] ChineseDayName = new string[] {
61. "初一","初二","初三","初四","初五","初六","初七","初八","初九","初十",
62. "十一","十二","十三","十四","十五","十六","十七","十八","十九","二十",
63. "廿一","廿二","廿三","廿四","廿五","廿六","廿七","廿八","廿九","三十"};
64. public static readonly string[] ChineseMonthName = new string[]
65. { "正", "二", "三", "四", "五", "六", "七", "八", "九", "十", "十一", "十二" };
66.}
第二个类为自定义格式化器:
1.using System;
2.using System.Collections.Generic;
3.using System.Text;
4.5.using System.Globalization;
6.using System.Threading;
7.8.public class ChineseCalendarFormatter : IFormatProvider, ICustomFormatter
9.{
10. //实现IFormatProvider 11. public object GetFormat(Type formatType)
12. {
13. if (formatType == typeof(ICustomFormatter))
14. return this;
15. else16. return Thread.CurrentThread.CurrentCulture.GetFormat(formatType);
17. }
18.19. //实现ICustomFormatter 20. public string Format(string format, object arg, IFormatProvider
formatProvider)
21. {
22. string s;
23. IFormattable formattable = arg as IFormattable;
24. if (formattable == null)
25. s = arg.ToString();
26. else27. s = formattable.ToString(format, formatProvider);
28. if (arg.GetType() == typeof(DateTime))
29. {
30. DateTime time = (DateTime)arg;
31. switch (format)
32. {
33. case "D": //长日期格式 34. s = String.Format("{0}年{1}月{2}",
35. ChineseCalendarHelper.GetYear(time),
36. ChineseCalendarHelper.GetMonth(time),
37. ChineseCalendarHelper.GetDay(time));
38. break;
39. case "d": //短日期格式 40. s = String.Format("{0}年{1}月{2}",
ChineseCalendarHelper.GetStemBranch(time),
41. ChineseCalendarHelper.GetMonth(time),
42. ChineseCalendarHelper.GetDay(time));
43. break;
44. case "M": //月日格式 45. s = String.Format("{0}月{1}",
ChineseCalendarHelper.GetMonth(time),
46. ChineseCalendarHelper.GetDay(time));
47. break;
48. case "Y": //年月格式 49. s = String.Format("{0}年{1}月",
ChineseCalendarHelper.GetYear(time),
50. ChineseCalendarHelper.GetMonth(time));
51. break;
52. default:
53. s = String.Format("{0}年{1}月{2}", ChineseCalendarHelper.GetYear(time),
54. ChineseCalendarHelper.GetMonth(time),
55. ChineseCalendarHelper.GetDay(time));
56. break;
57. }
58. }
59. return s;
60. }
61.}
这段代码中间处理格式那部份稍做改进,就可以支持更多的日期格式。
有了这两段代码为原型,要实现计算和显示一个日期的农历日期及其它功能,基本上就很容易了。
三、VBScript 代码:
1.
2.'Wonsoft, Welcome to visit my web http://wonsoft.cn
3.'***********************************************
4.' 类名称:ChinaDay
5.' 用途:
6.' 根据输入的日期计算该日期的农历天干地支及当年属相
7.' 使用方法:
8.' 第一个参数为输入参数,不填写默认为当日,
9.' 只计算1921-2-8之后的日期
10.' ##-------------------------------------------##
11.' Dim objChinaDay
12.' Dim sDay, sWeekDay, sChinaDay, sChinaYear,sChinaAni
13.' Set objChinaDay = New ChinaDay
14.' Call objChinaDay.Action("",sDay,sWeekDay,sChinaYear,sChinaDay,sChinaAni)
15.' Response.Write sDay&"
"
16.' Response.Write sWeekDay&"
"
17.' Response.Write sChinaYear&"
"
18.' Response.Write sChinaDay&"
"
19.' Response.Write sChinaAni&"
"
20.' ##-------------------------------------------##
21.' Copyright: 本代码非原创,是2001年收集的,原作者未知。
22.' License:Free
23.'*******************************************************
24.Class ChinaDay
25.26.Dim arrWeekName(7), MonthAdd(11), NongliData(99)
27.Dim arrTianGan(9), arrDiZhi(11), arrShuXiang(11), arrDayName(30), arrMonName(12)
28.Dim curTime, curYear, curMonth, curDay, curWeekday
29.Dim i, m, n, k, isEnd, bit, TheDate
30.31.'初始化数据
32.Sub Class_Initialize()
33.'---------------------------------------------------
34.'定义显示字串
35.36.'星期名
37.arrWeekName(0) = "*"
38.arrWeekName(1) = "星期日"
39.arrWeekName(2) = "星期一"
40.arrWeekName(3) = "星期二"
41.arrWeekName(4) = "星期三"
42.arrWeekName(5) = "星期四"
43.arrWeekName(6) = "星期五"
44.arrWeekName(7) = "星期六"
45.46.'天干名称
47.arrTianGan(0) = "甲"
48.arrTianGan(1) = "乙"
49.arrTianGan(2) = "丙"
50.arrTianGan(3) = "丁"
51.arrTianGan(4) = "戊"
52.arrTianGan(5) = "己"
53.arrTianGan(6) = "庚"
54.arrTianGan(7) = "辛"
55.arrTianGan(8) = "壬"
56.arrTianGan(9) = "癸"
57.58.'地支名称
59.arrDiZhi(0) = "子"
60.arrDiZhi(1) = "丑"
61.arrDiZhi(2) = "寅"
62.arrDiZhi(3) = "卯"
63.arrDiZhi(4) = "辰"
64.arrDiZhi(5) = "巳"
65.arrDiZhi(6) = "午"
66.arrDiZhi(7) = "未"
67.arrDiZhi(8) = "申"
68.arrDiZhi(9) = "酉"
69.arrDiZhi(10) = "戌"
70.arrDiZhi(11) = "亥"
71.72.'属相名称
73.arrShuXiang(0) = "鼠"
74.arrShuXiang(1) = "牛"
75.arrShuXiang(2) = "虎"
76.arrShuXiang(3) = "兔"
77.arrShuXiang(4) = "龙"
78.arrShuXiang(5) = "蛇"
79.arrShuXiang(6) = "马"
80.arrShuXiang(7) = "羊"
81.arrShuXiang(8) = "猴"
82.arrShuXiang(9) = "鸡"
83.arrShuXiang(10) = "狗"
84.arrShuXiang(11) = "猪"
85.86.'农历日期名
87.arrDayName(0) = "*"
88.arrDayName(1) = "初一"
89.arrDayName(2) = "初二"
90.arrDayName(3) = "初三"
91.arrDayName(4) = "初四"
92.arrDayName(5) = "初五"
93.arrDayName(6) = "初六"
94.arrDayName(7) = "初七"
95.arrDayName(8) = "初八"
96.arrDayName(9) = "初九"
97.arrDayName(10) = "初十"
98.arrDayName(11) = "十一"
99.arrDayName(12) = "十二"
100.arrDayName(13) = "十三"
101.arrDayName(14) = "十四"
102.arrDayName(15) = "十五"
103.arrDayName(16) = "十六"
104.arrDayName(17) = "十七"
105.arrDayName(18) = "十八"
106.arrDayName(19) = "十九"
107.arrDayName(20) = "二十"
108.arrDayName(21) = "廿一"
109.arrDayName(22) = "廿二"
110.arrDayName(23) = "廿三"
111.arrDayName(24) = "廿四"
112.arrDayName(25) = "廿五"
113.arrDayName(26) = "廿六"
114.arrDayName(27) = "廿七"
115.arrDayName(28) = "廿八"
116.arrDayName(29) = "廿九"
117.arrDayName(30) = "三十"
118.119.'农历月份名
120.arrMonName(0) = "*"
121.arrMonName(1) = "正"
122.arrMonName(2) = "二"
123.arrMonName(3) = "三"
124.arrMonName(4) = "四"
125.arrMonName(5) = "五"
126.arrMonName(6) = "六"
127.arrMonName(7) = "七"
128.arrMonName(8) = "八"
129.arrMonName(9) = "九"
130.arrMonName(10) = "十"
131.arrMonName(11) = "十一"
132.arrMonName(12) = "腊"
133.134.'---------------------------------------------------------
135.136.'公差数据定义
137.138.'公历每月前面的天数
139.MonthAdd(0) = 0
140.MonthAdd(1) = 31
141.MonthAdd(2) = 59
142.MonthAdd(3) = 90
143.MonthAdd(4) = 120
144.MonthAdd(5) = 151
145.MonthAdd(6) = 181
146.MonthAdd(7) = 212
147.MonthAdd(8) = 243
148.MonthAdd(9) = 273
149.MonthAdd(10) = 304
150.MonthAdd(11) = 334
151.152.'农历数据
153.NongliData(0) = 2635
154.NongliData(1) = 333387
155.NongliData(2) = 1701
156.NongliData(3) = 1748
157.NongliData(4) = 267701
158.NongliData(5) = 694
159.NongliData(6) = 2391
160.NongliData(7) = 133423
161.NongliData(8) = 1175
162.NongliData(9) = 396438
163.NongliData(10) = 3402
164.NongliData(11) = 3749
165.NongliData(12) = 331177
166.NongliData(13) = 1453
167.NongliData(14) = 694
168.NongliData(15) = 201326
169.NongliData(16) = 2350
170.NongliData(17) = 465197
171.NongliData(18) = 3221
172.NongliData(19) = 3402
173.NongliData(20) = 400202
174.NongliData(21) = 2901
175.NongliData(22) = 1386
176.NongliData(23) = 267611
177.NongliData(24) = 605
178.NongliData(25) = 2349
179.NongliData(26) = 137515
180.NongliData(27) = 2709
181.NongliData(28) = 464533
182.NongliData(29) = 1738
183.NongliData(30) = 2901
184.NongliData(31) = 330421
185.NongliData(32) = 1242
186.NongliData(33) = 2651
187.NongliData(34) = 199255
188.NongliData(35) = 1323
189.NongliData(36) = 529706
190.NongliData(37) = 3733
191.NongliData(38) = 1706
192.NongliData(39) = 398762
193.NongliData(40) = 2741
194.NongliData(41) = 1206
195.NongliData(42) = 267438
196.NongliData(43) = 2647
197.NongliData(44) = 1318
198.NongliData(45) = 204070
199.NongliData(46) = 3477
200.NongliData(47) = 461653
201.NongliData(48) = 1386
202.NongliData(49) = 2413
203.NongliData(50) = 330077
204.NongliData(5