近期在做一个工作日历,想在日历上设置工作日,显示请假、加班等相关信息,显示农历日期信息(包括农历日期、节日
、节气)、公历信息(节假期)。
不能不说,http://www.cnblogs.com/hocylan/archive/2007/11/16/961218.html 这篇博文对我的帮助良多。但是
我也发现了一些问题。包括几下3个点:
首先,就是"0100 除夕"这个节日并不能被显示出来,无论是对于农历12月29是一年最后一天,还是12月30是最后一天。
我的解决方法是将这条数据改为"1230 除夕",然后在获取农历节日信息的时候判断这天和下一天的农历日期的年份是否相等,
如果不等,如一个是2012,一个是2013,那么这天就是“除夕”了。
其次,在判断几月的第几周的周几为某个节日时,
if (Convert.ToInt32(s[1]) == month && Convert.ToInt32(s[2]) == num && Convert.ToInt32(s[2]) == week)
这句应该改成:
if (Convert.ToInt32(s[1]) == month && Convert.ToInt32(s[2]) == num && Convert.ToInt32(s[3]) == week)。
博主可能疏忽了。
最后,我想在提到的这篇博文的基础上补充关于24节气的显示。
节气指二十四时节和气候,是中国古代发明的一种用来指导农事的历法。中国古代利用土圭实测日晷,
将每年日影最长定为日至(又称日长至、长至、夏至),日影最短为日短至(又称短至、冬至),在春秋两季
各有一天的昼夜时间长短相等,便定为春分和秋分,在商朝时只有四个节气,到了周朝时发展到了八个,
直到西汉时才成为了现在的二十四节气。在史记太史公自序中就有提到阴阳、四时、八位、十二度、二十
四节气的概念。
二十四节气每一个分别相应于太阳在黄道上每运动15°所到达的一定位置。二十四节气又分为12个中气
和12个节气,一一相间。二十四节气反映了太阳的周年视运动,所以在公历中它们的日期是基本固定的,
上半年的节气在6日,中气在21日,下半年的节气在8日,中气在23日,二者前后不差1~2日。将一回归年
的长度等分成24份,从冬至开始,等间隔地依次相同安排各个中气和节气。这种方法叫做平气。由于太阳周
年视运动不均匀,按太阳黄经每移行15°的节气是非等间距的,此法称为定气。定气使用于历法计算中。因
为两个节气的时间长于一个朔望月(见月)的时间,所以可能出现一个月内只有一个节气或一个中气的情况。
从西汉的《太初历》起,规定遇到没有中气的月份定为上月的闰月。这种置闰原则沿用至今。
二十四节气的命名反应了季节、气侯现象、气侯变化三种。反应季节的是立春、春分、立夏、夏至、立
秋、秋分、立冬、冬至,又称八位; 反应气侯现象的是惊蛰、清明、小满、芒种; 反应气侯变化的有雨水、
谷雨、小暑、大暑、处暑、白露、寒露、霜降、小雪、大雪、小寒、大寒。
现代人根据太阳在黄道上的位置,准确地确定了二十四节气的具体时间。 节气的具体时间甚至精确到了几时几分,为了避免不同时间点显示的节气不同,我把节气的时间精确到天而已。
代码如下:
/// <summary>
/// 计算某一年的节气,返回节气
/// </summary>
/// <param name="dt"></param>
/// <returns></returns>
private string ComputeSolarTerm(DateTime dt)
{
List<string> solar = new List<string>();
int year = dt.Year;
for (int month = 1; month < 13; month++)
{
for (int n = month * 2 - 1; n <= month * 2; n++)
{
double Termdays = Term(year, n, true);
double mdays = AntiDayDifference(year, Math.Floor(Termdays));
double sm1 = Math.Floor(mdays / 100);
int hour = (int)Math.Floor((double)Tail(Termdays) * 24);
int minute = (int)Math.Floor((double)(Tail(Termdays) * 24 - hour) * 60);
int tMonth = (int)Math.Ceiling((double)n / 2);
int day = (int)mdays % 100;
string jqtime = Convert.ToString(new DateTime(year, tMonth, day, hour, minute, 0).ToString("yyyyMMdd")); //这里把节气的日期精确到天
string jqname = solarTerm[n - 1];
solar.Add(jqtime+" "+jqname);
}
}
string str = @"(\d{4})(\d{2})(\d{2})([\s\*])(.+)$"; //匹配的正则表达式
System.Text.RegularExpressions.Regex re = new System.Text.RegularExpressions.Regex(str);
for (int i = 0; i < solar.Count;i++ )
{
string[] s = re.Split(solar[i]);
if (Convert.ToInt32(s[1]) == dt.Year && Convert.ToInt32(s[2]) == dt.Month && Convert.ToInt32(s[3]) == dt.Day)
{
return s[5];
}
}
return "";
}
#region 节气计算中用到的方法
/// <summary>
/// 返回y年第n个节气(如小寒为1)的日差天数值(pd取值真假,分别表示平气和定气)
/// </summary>
/// <param name="y"></param>
/// <param name="n"></param>
/// <param name="pd"></param>
/// <returns></returns>
private double Term(int y, int n, bool pd)
{
//儒略日
double juD = y * (365.2423112 - 6.4e-14 * (y - 100) * (y - 100) - 3.047e-8 * (y - 100)) + 15.218427 * n + 1721050.71301;
//角度
double tht = 3e-4 * y - 0.372781384 - 0.2617913325 * n;
//年差实均数
double yrD = (1.945 * Math.Sin(tht) - 0.01206 * Math.Sin(2 * tht)) * (1.048994 - 2.583e-5 * y);
//朔差实均数
double shuoD = -18e-4 * Math.Sin(2.313908653 * y - 0.439822951 - 3.0443 * n);
double vs = (pd) ? (juD + yrD + shuoD - EquivalentStandardDay(y, 1, 0) - 1721425) : (juD - EquivalentStandardDay(y, 1, 0) - 1721425);
return vs;
}
/// <summary>
/// 返回阳历y年日差天数为x时所对应的月日数(如y=2000,x=274时,返回1001(表示10月1日,即返回100*m+d))
/// </summary>
/// <param name="y"></param>
/// <param name="x"></param>
/// <returns></returns>
private double AntiDayDifference(int y, double x)
{
int m = 1;
for (int j = 1; j <= 12; j++)
{
int mL = DayDifference(y, j + 1, 1) - DayDifference(y, j, 1);
if (x <= mL || j == 12)
{
m = j;
break;
}
else
x -= mL;
}
return 100 * m + x;
}
/// <summary>
/// 返回x的小数尾数,若x为负值,则是1-小数尾数
/// </summary>
/// <param name="x"></param>
/// <returns></returns>
private double Tail(double x)
{
return x - Math.Floor(x);
}
/// <summary>
/// 返回等效标准天数(y年m月d日相应历种的1年1月1日的等效(即对Gregorian历与Julian历是统一的)天数)
/// </summary>
/// <param name="y"></param>
/// <param name="m"></param>
/// <param name="d"></param>
/// <returns></returns>
private double EquivalentStandardDay(int y, int m, int d)
{
//Julian的等效标准天数
double v = (y - 1) * 365 + Math.Floor((double)((y - 1) / 4)) + DayDifference(y, m, d) - 2;
if (y > 1582)
{//Gregorian的等效标准天数
v += -Math.Floor((double)((y - 1) / 100)) + Math.Floor((double)((y - 1) / 400)) + 2;
}
return v;
}
/// <summary>
/// 返回阳历y年m月d日的日差天数(在y年年内所走过的天数,如2000年3月1日为61)
/// </summary>
/// <param name="y"></param>
/// <param name="m"></param>
/// <param name="d"></param>
/// <returns></returns>
private int DayDifference(int y, int m, int d)
{
int ifG = IfGregorian(y, m, d, 1);
int[] monL = { 0, 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 };
if (ifG == 1)
if ((y % 100 != 0 && y % 4 == 0) || (y % 400 == 0))
monL[2] += 1;
else
if (y % 4 == 0)
monL[2] += 1;
int v = 0;
for (int i = 0; i <= m - 1; i++)
{
v += monL[i];
}
v += d;
if (y == 1582)
{
if (ifG == 1)
v -= 10;
if (ifG == -1)
v = 0; //infinity
}
return v;
}
// 判断y年m月(1,2,..,12,下同)d日是Gregorian历还是Julian历
//(opt=1,2,3分别表示标准日历,Gregorge历和Julian历),是则返回1,是Julian历则返回0,
// 若是Gregorge历所删去的那10天则返回-1
private int IfGregorian(int y, int m, int d, int opt)
{
if (opt == 1)
{
if (y > 1582 || (y == 1582 && m > 10) || (y == 1582 && m == 10 && d > 14))
return (1); //Gregorian
else
if (y == 1582 && m == 10 && d >= 5 && d <= 14)
return (-1); //空
else
return (0); //Julian
}
if (opt == 2)
return (1); //Gregorian
if (opt == 3)
return (0); //Julian
return (-1);
}
#endregion