`
runfeel
  • 浏览: 928504 次
文章分类
社区版块
存档分类
最新评论

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

 
阅读更多

【接上篇】

上述计算星期的方法虽然步骤简单,但是每次都要计算两个日期的时间差,不是非常方便。如果能够有一个公式可以直接根据日期计算出对应的星期岂不是更好?幸运的是,这样的公式是存在的。此类公式的推导原理仍然是通过两个日期的时间差来计算星期,只是通过选择一个特殊的日期来简化公式的推导。这个所谓的特殊日期指的是某一年的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天,

    2017年日历每月一张+完整打印 含阴历

    公历(阳历)是指按照地球绕太阳公转一周的周期来计算时间的历法,也称为格里历(Gregorian calendar),以1月1日作为新的一年的开始,以12月31日作为一年的结束。公历有固定的天数,平年为365天,闰年为366天。 ...

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

    而格里历,也称为公历,是我们日常生活中广泛使用的日历系统,基于太阳年周期,包括闰年规则。 在Qt5.8中,我们可以使用`QDate`和`QDateTime`类来处理格里历日期,而`QDateTime`类提供了`toJulianDay()`和`...

    格里历日转儒略日

    格里历日转儒略日

    高精度天文算法的万年历

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

    儒略日转格里历日

    儒略日转格里历日

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

    本文将深入探讨如何在MATLAB中进行儒略日(Julian Day)与全球定位系统时间(GPS Time)之间的转换,以及儒略日转换为格里历日期的方法。 首先,让我们了解儒略日。儒略日是一种连续的日数计数系统,从公元前4713年...

    java常用英语

    - **Gregorian Calendar**: 格里历,现代公历。 - **Concatenate**: 连接,将两个或多个字符串合并成一个字符串。 - **Stack**: 栈,一种数据结构,遵循先进后出的原则。 - **Remove**: 移除,从集合或序列中删除...

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

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

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

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

    小学数学数学故事自己就是一本活日历

    总结来说,"活日历"的概念是数学在日常生活中的应用之一,通过学习和掌握这些计算公式,小学生能够增强对数学的兴趣,同时提高问题解决能力。这些知识不仅是理论性的,还具有实用价值,可以在日常生活中随时应用,让...

    一个非常简洁的计算星期几的程序

    蔡勒公式非常适合于1582年10月15日之后的情形,因为这是格里高利历(格里历或公历)开始实施的日子。在此之前使用的是儒略历。格里高利历是由罗马教皇格里高利十三世对儒略历进行修改后提出的,它取消了1582年10月5...

    愚人节节日介绍ppt模板b.pptx

    - **法国纪年法改革说**:1564年,法国率先采用了新的纪年方法——格里历(即现行的公历),以1月1日作为新年伊始,替代了之前的4月1日。这项改革遭到了一些守旧者的反对,他们坚持沿用旧历法。随着时间推移,那些...

    日柱计算公式研究及利用python语言进行推导验证——求解公历任一日干支的数学公式(高氏日柱公式).pdf

    公历(格里历)由1582年开始使用,它继承了儒略历大、小月的设置方式,一年分为12个月,其中单数月为大月,双数月为小月,2月根据是否是闰年而有不同的天数。闰年规则是,公历年份如果能被4整除则为闰年,但整百数年...

    C语言函数练习题3[文].pdf

    闰年是为了调整公历(格里历)与地球实际公转周期的差异。根据公历规则,闰年的判断标准是: - 普通闰年:能被4整除但不能被100整除的年份。 - 世纪闰年:能被400整除的年份,或者能被100整除但不能被400整除的年份...

    Javascript农历与公历相互转换的简单实例

    而公历,也称格里历或阳历,是一种太阳历,以地球绕太阳公转周期为基础,平年365天,闰年366天。本文的实例代码将为我们提供一种方法,可以将农历日期转换为公历日期,反之亦然。 首先,我们需要了解文中的代码片段...

    2019年浙江杭州市气象服务中心招聘模拟试题及答案解析.docx

    3. 中国历法历史:汉武帝时期的《太初历》是中国古代有记录的第一部完整历法,但它与现代世界通行的公历(格里历)并不相同。公历是基于罗马儒略历和格里高利历发展而来,与《太初历》有本质区别。 4. 宏观经济调控...

Global site tag (gtag.js) - Google Analytics