这里是搬家过来的原创文章,本文是09年5月份写的(还没写完:重要的实现算法还没介绍),有点旧了,但总算有个家。于是发到这里。
上周由于工作需要,要做一个带标注的日历查询的JS程序。到网上也搜了不少日历相关的JS代码,但是发现都比较复杂,完全不适合本次开发的需要,于是乎萌生了自己开发一个日历算法的想法。
虽然这个程序的算法难度一般,但是还是冒死放出来溜溜,免得今后忘记了。大家都知道,日历主要是解决星期和日期之间的关系,另外就是一个排版的问题,如何将日期和星期一一对应,这个也是要考虑的重点。那么总结一下要解决的问题:
- 给定日期,求出对应星期
- 给定日期,求出该月的日历。
一、给定日期,求星期
- 首先,要知道星期怎么来的,星期从哪天算起,即找到一个计算“原点”,这样之后星期计算就可以有依据了。对于这个问题,其实可以采取反推的办法来实现,找到比如公元元年的星期日(便于求余计算直接得到星期数)的日期。
- 其次,在知道第一个星期对应的年月日的基础上,求得当前日期是有星期以来多少天,然后用这个间隔天数对一周的天数7取模运算即可求出当前星期几。
- 因为存在闰年,所以还需要计算有多少个闰年,把这个加上去才能得到间隔天数。
其实这个算法早已存在,下面就这个最常见的公式:
W = [Y-1] + [(Y-1)/4] – [(Y-1)/100] + [(Y-1)/400] + D Y是年份数,D是这一天在这一年中的累积天数,也就是这一天在这一年中是第几天,用个计算机术语就叫相对年初的第0天的偏移量了。
这里就不对这个公式进行推导了,详细推导过程可以到这里下载PDF文档(以下简称PDF):http://download.csdn.net/source/1308663 这个可能要扣一点资源费(莫BS我),表示支持俺的辛苦搜集。
有了上面的公式,接下来就是获得公式里的参数了,其中Y比较容易(地球人人都知道),麻烦的地方在D(其实PDF中Zeller给出了比较简单的算法),不过这里偶比较好奇,所以非要实现D的算法。
为了求D(也就是偏移量),需要计算当前月之前所有月的∑值,以及当前日在当月的偏移量(简称M),那么
D = ∑m + M
其中,∑m 是当年第一月到M所在月前一月的各月天数之和,这个比较简单,关键是确定每个月的天数。原本十二个月每个月天数既定,除了二月份的闰年和平年不一样,这就要根据是否闰年来判断了。根据公里年闰年的算法:则有下面大家所熟知的通用公式。
LeapYear = ( Year / 4 == 0 && Year / 100 !=0) || Year / 400 == 0
下面是给定月份天数算法的C程序实现:
// 给定年月获取当月天数
int getMDay(int y, int m)
{
int mday[12]= {31,28,31,30,31,30,31,31,30,31,30,31} ;
if (( 0 == y % 4 && 0 != y % 100) || 0 == y % 400)
mday[1] = 29;
return mday[m-1];
}
有了这个函数,就可以准确得知某日所在月份的天数了。那么计算D就好办了,这样给定日期求星期的算法就完全解决,看代码:
// 获取星期数,0对应星期天 ,1对应星期一,其他类推
int week(int y, int m, int d)
{
int wk ,date_offset=d,im,tmp=0;
if (m <= 12 && m >=1 )
{
for(im=1; im < m; ++im)
{
date_offset += getMDay(y,im);
}
}
/*根据日期计算星期的公式*/
wk = (y-1 + (y-1)/4 - (y-1)/100 + (y-1)/400 + date_offset)%7;
return wk;
}
二、给定日期,求出该月的日历
其实上面的都不是本文的重点,本文重点在于如何生成一个日历来,当然方法很多,这里我也给出一个自己的思路,如有雷同,纯属巧合!
由于时间问题,今天就不写了,给下次写留点空间。(看这篇文章的算法实现前传)