`
ldb19890624
  • 浏览: 226894 次
  • 性别: Icon_minigender_1
  • 来自: 北京
文章分类
社区版块
存档分类
最新评论

算法系列之十七:日历生成算法-中国公历(格里历)(下)

 
阅读更多

【接上篇】

上述计算星期的方法虽然步骤简单,但是每次都要计算两个日期的时间差,不是非常方便。如果能够有一个公式可以直接根据日期计算出对应的星期岂不是更好?幸运的是,这样的公式是存在的。此类公式的推导原理仍然是通过两个日期的时间差来计算星期,只是通过选择一个特殊的日期来简化公式的推导。这个所谓的特殊日期指的是某一年的1231日这天刚好是星期日这种情况。选择这样的日子有两个好处,一个是计算上可以省去计算标准日期这一年的剩余天数,另一个是计算出来的日期差余数是几就是星期几,不需要再计算星期的差值。人们知道公元元年的11日是星期一,那么公元前1年的1231日就是星期日,用这一天作为标准日期,就可以只计算整数年的时间和日期所在的年积累的天数,这个星期公式就是:

w = (L * 366 + N * 365 + D) % 7 (公式 2)

公式中的L是从公元元年到ymd日所在的年之间的闰年次数,N是平常年次数,Dy年内的积累天数。将整年数y - 1 = L + N带入上式,可得:

w = ( (y - 1) * 365 + L + D) % 7 (公式 3)

根据闰年规律,从公元元年到y年之间的闰年次数是可以计算出来的,即:

L带入公式2,得到星期w的最终计算公式:

还以2005531日为例,利用公式5计算w的值为:

得到2005531日是星期二,和前面的计算方法得到的结果一致。根据上述分析,可得写出使用公式5计算星期的算法实现:

146int TotalWeek(int year, int month, int day)

147{

148 int d = CalcYearPassedDays(year, month, day);

149 int y = year - 1;

150 int w = y * DAYS_OF_NORMAL_YEAR + y / 4 - y / 100 + y / 400 + d;

151

152 return w % 7;

153}

公式5的问题在于计算量大,不利于口算星期结果。于是人们就在公式5的基础上继续推导更简单的公式。德国数学家克里斯蒂安·蔡勒(Christian Zeller, 1822- 1899)在1886年推导出了著名的为蔡勒(Zeller)公式:

对计算出的w值除以7,得到的余数就是星期几,如果余数是0,则为星期日。蔡勒公式中各符号的含义如下:

w :星期;

c:世纪数 – 1的值,如21世纪,则= 20

m:月数,的取值是大于等于3,小于等于14。在蔡勒公式中,某年的1月和2月看作上一年的13月和14月,比如200121日要当成2000年的141日计算;

y:年份,取公元纪念的后两位,如1998年,= 982001年,= 1

d :某月内的日数

为了方便口算,人们通常将公式6中的一项改成

。目前人们普遍认为蔡勒公式是计算某一天是星期几的最好的公式。但是蔡勒公式有时候可能计算出的结果是负数,需要对结果+7进行修正。比如200671日,用蔡勒公式计算出的结果是 -1,实际上这天是星期六。根据前面分析的结果整理出的蔡勒公式算法实现如下:

155int ZellerWeek(int year, int month, int day)

156{

157 int m = month;

158 int d = day;

159

160 if(month <= 2) /*对小于2的月份进行修正*/

161 {

162 year--;

163 m = month + 12;

164 }

165

166 int y = year % 100;

167 int c = year / 100;

168

169 int w = (y + y / 4 + c / 4 - 2 * c + (13 * (m + 1) / 5) + d - 1) % 7;

170 if(w < 0) /*修正计算结果是负数的情况*/

171 w += 7;

172

173 return w;

174}

蔡勒公式(公式6)和前面提到的公式5都只适用于格里历法。罗马教皇在1582年修改历法,将105日指定为1015日,从而正式废止儒略历法,开始启用格里历法。因此,上述求星期几的公式只适用于15821015日之后的日期,对于1582年将104日之前的日期,蔡勒也推导出了适用与儒略历法的星期计算公式:

公式7适用于对1582104日之前的日期计算星期,1582105日与15821015日之间的日期是不存在的,因为它们都是同一天。

格里历历法简单,除二月外每月天数固定,二月则根据是否是闰年确定是28天还是29天,每天的星期数可以通过蔡勒公式(公式6)计算,有了这些信息,就可以按照一定的排版格式将某一年的日历打印出来。排版打印的算法非常简单,就是按照顺序打印12个月的月历,因此,打印月历的函数就是输出算法的重点。代码没什么特别之处,就是用一些小技巧确定每个月的第一天的开始位置,打印月历的核心代码如下:

229void PrintMonthCalendar(int year, int month)

230{

231 int days = GetDaysOfMonth(year, month); /*确定这个月的天数*/

232 if(days <= 0)

233 return;

234

235 PrintMonthBanner(nameOfMonth[month - 1]);

236 PrintWeekBanner();

237 int firstDayWeek = ZellerWeek(year, month, 1);

238 InsertRowSpace(firstDayWeek);

239 int week = firstDayWeek;

240 int i = 1;

241 while(i <= days)

242 {

243 printf("%-10d", i);

244 if(week == 6) /*到一周结束,切换到下一行输出*/

245 {

246 SetNextRowStart();

247 }

248 i++;

249 week = (week + 1) % 7;

250 }

251}

GetDaysOfMonth()函数其实就是从daysOfMonth表中查一下每月的天数,如果是闰年,则对二月的天数修正(+1),daysOfMonth表定义如下:

int daysOfMonth[MONTHES_FOR_YEAR] = { 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31};

计算星期不必对每一天都计算一次,只要对每个月的第一天计算一次就可以了,以后的日期可以用 week = (week + 1) % 7 直接推算出星期几。下面就是我们的算法打印输出的效果:

********************************************************************************

Calendar of 2012

********************************************************************************

----------January----------

Sunday Monday Tuesday Wednesday Thursday Friday Saturday

1 2 3 4 5 6 7

8 9 10 11 12 13 14

15 16 17 18 19 20 21

22 23 24 25 26 27 28

29 30 31

----------February----------

Sunday Monday Tuesday Wednesday Thursday Friday Saturday

1 2 3 4

5 6 7 8 9 10 11

12 13 14 15 16 17 18

19 20 21 22 23 24 25

26 27 28 29

----------March----------

Sunday Monday Tuesday Wednesday Thursday Friday Saturday

1 2 3

4 5 6 7 8 9 10

11 12 13 14 15 16 17

18 19 20 21 22 23 24

25 26 27 28 29 30 31

……

小知识2儒略历和格里历

在公元15821015日之前,人们使用的历法是源自古罗马的儒略历,儒略历的置闰规则就是四年一闰,但是没有计算每年多出来的0.0078天,这样从公元前46年到公元1582年一共累积多出了10天,为此,当时的教皇格里十三世将1582105日人为指定为1015日,并开始启用新的置闰规则,这就是后来沿用至今的格里历。

小知识3约化儒略日

由于儒略日数字位数太多,国际天文联合会于19738月决定对其修正,采用约化儒略日(MJD)进行天文计算,定义MJD = JD – 2400000.5MJD相应的起始点是18581117 0:00

小知识417529月到底是怎么回事儿

如果你用的操作系统是unixlinux,在控制台输入以下命令:

#cal 9 1752

你会看到这样一个奇怪的月历输出:

September 1752

Su Mo Tu We Th Fr Sa

1 2 14 15 16

17 18 19 20 21 22 23

24 25 26 27 28 29 30

1752年的9月缺了11天,到底怎么回事儿?这其实还是因为从儒略历到格里历的转换造成的。1582105日,罗马教皇格里十三世宣布启用更为精确的格里历,但是整个欧洲大陆并不是所有国家都立即采用格里历,比如大英帝国就是直到17529月议会才批准采用格里历,所以大英帝国及其所有殖民地的历法一直到17529月才发生跳变,“跟上”了格里历。德国和荷兰到了1698年才采用格里历,而俄罗斯则直到1918年革命才采用格里历。Linuxcal指令起源与最初AT&TUNIX,当然采用的是美国历法,但是美国历史太短,再往前就只能采用英国历法,所以cal指令的结果就成了这样。对于采用格里历的国家来说,只要知道158210月发生了日期跳变就行了,可以不用关心17529月到底是怎么回事儿。但是对于研究历史和考古的人来说,就必需要了解这个历史,搞清楚每个欧洲国家改用格里历的年份,否则就可能在一些问题上出错。在欧洲研究历史,你会发现很多事件都是有多个时间版本的,比如大科学家牛顿的生日就有两个时间版本,一个是按照儒略历历法的16421225日,另一个是格里历历法的164314日,对于英国人来说,1752年之前都是按照儒略历计算的,所以英国的史书可能会记载牛顿出生在圣诞节,这也没什么可奇怪的。

分享到:
评论

相关推荐

    Fortran APPJED 儒略日 儒略历 格里历 公元历互转

    第三个表示格里历.参数是公元历.注意年份需要补足8位. E:\Fortran_G95&gt;appjed 00001582-10-15.00.00.00.000 参数个数: 1 2299160.50000000 577736.000000000 JDL: 0Y 1582-10-05.00:00:00.000 E:\Fortran_G95&gt;...

    C++算法系列之日历生成的算法代码

    日历在我们的生活中扮演着十分重要的角色,上班、上学、约会都离不开日历。...我国的官方历法是中国公历,也就是世界通用的格里历(Gregorian Calendar),中国公历的年分为平常年和闰年,平常年一年是365天,

    格里历日转儒略日

    格里历日转儒略日

    高精度天文算法的万年历

    梭万年历是世界上第一款采用现代高精度天文历算算法制作的公历、农历、伊斯兰历(回历)三历对照、信息丰富、年代特长的多功能万年历。时间跨度长达4000年(起自格里历开始实行年之后的1583年,即明朝万历11年,止于...

    儒略日转格里历日

    儒略日转格里历日

    Qt儒略日和格里历的相互转换

    利用Qt5.8编程实现了儒略日和儒略日的相互转化,计算时间从-4712年1月1.5日开始,没有结束时间限制。

    用VC6或VS2010编辑源程序。软件用于显示日历和星空

    此软件用于显示日历和星空,日历从公元前850年(我国历法开始)到公元9999年,公历(包括格里历和儒略历)与农历对照,星期,生肖,儒略日,年月日时,24节气的交节时刻。梅雨,夏三伏,冬三九也准确显示。当然,还会显示...

    日梭万年历(高精度的万年历)V5.0绿色免费版

    日梭万年历是第一款采用现代高精度天文历算算法生成的公历、农历、伊斯兰历(回历)三历对照、信息丰富、年代特长的多功能万年历。时间跨度长达4000年(起自格里历开始实行年之后的1583年,即明朝万历11年,止于公元...

    DOS万年历

    公历与农历对照,天干地支,廿四节气交节时刻,朔望时刻,日月食,儒略日,生肖星座,公历节日,传统节日和名人纪念日,以及冬九九,夏三伏,梅雨季节等提示,公历与格里历和儒略历的变换,还有人体生物节律查询,倒...

    格里历、儒略日、GPS时、Glonass时、北斗时、年积日互转

    C# 时间转换 代码

    欢迎您使用日梭万年历

    日梭万年历是世界上第一款采用现代高精度天文历算算法制作的公历、农历、伊斯兰历(回历)三历对照、信息丰富、年代特长的多功能万年历。时间跨度长达4000年(起自格里历开始实行年之后的1583年,即明朝万历11年,止...

    日梭万年历2008 Beta1

    采用现代高精度天文历算算法制作的公历、农历、伊斯兰历(回历)三历对照、信息丰富、年代特长的多功能万年历。时间跨度长达4000年(起自格里历开始实行年之后的1583年,即明朝万历11年,止于公元5582年),可查询...

    信息全面十分好用的日梭万年历

    日梭万年历是世界上第一款采用现代高精度天文历算算法制作的公历、农历、伊斯兰历(回历)三历对照、信息丰富、年代特长的多功能万年历。时间跨度长达4000年(起自格里历开始实行年之后的1583年,即明朝万历11年,...

    日梭万年历

    &lt;br&gt; 日梭万年历是世界上第一款采用现代高精度天文历算算法制作的公历、农历、伊斯兰历(回历)三历对照、信息丰富、年代特长的多功能万年历。时间跨度长达4000年(起自格里历开始实行年之后的1583年,即明朝...

    日梭万年历(带农历,星座,节气简直就是一个万能的)

    世界上第一款采用现代高精度天文历算算法制作的公历、农历、伊斯兰历(回历)三历对照、信息丰富、年代特长的多功能万年历。时间跨度长达4000年(起自格里历开始实行年之后的1583年,即明朝万历11年,止于公元5582年...

    日梭万年历 2008 Beta 1

    日梭万年历是世界上第一款采用现代高精度天文历算算法制作的公历、农历、伊斯兰历(回历)三历对照、信息丰富、年代特长的多功能万年历。时间跨度长达4000年(起自格里历开始实行年之后的1583年,即明朝万历11年,...

    南方天文万年历8.4

    本程序是研究中国历史、年代、历法、查阅史日的工具,时间跨度为公元前850年到公元后2100年近三千年时间,内容包括阴阳历互求、二十四节气平定两方面时刻、朔望平定两方面时刻、月食日食推定、食分与各限时刻、历史...

    日梭万年历 5.0版 免费软件

    时间跨度长达4000年(起自格里历开始实行年之后的1583年,即明朝万历11年,止于公元5582年),可查询的信息包括:每日的公历、农历、伊斯兰历(回历)日期,节气、节假日,年月日的天干地支、节气的交节时刻、月相和...

    MATLAB儒略日与GPS时的相互转换

    基于matlab的儒略日与GPS时的相互转换,同时也有儒略日转换为格里历的转换

    格里利和儒略历与儒略日间的相互转化matlab程序

    支持格里历和儒略历对儒略日间的相互转化,程序完整。本人测试通过,切无错误。

Global site tag (gtag.js) - Google Analytics