Algorithm Series Seventeen: Calendar Generation Algorithm-Chinese Gregorian Calendar (Gregorian Calendar) (Part 1)

        The calendar plays a very important role in our lives. It is inseparable from work, school, and appointments. Every year at the beginning of the New Year, people have to change a new calendar. Do you want to know how so many days in the next year are determined? Why last year's National Day was Friday and this year's National Day was Wednesday? Let's study the calendar algorithm. This article will introduce the layout rules of the calendar, the calculation method for determining the day of the week, and how to print the annual calendar for a certain year on the computer.

        To study the calendar algorithm, we must first know the rules of the calendar, which is the calendar. The so-called calendar refers to the calculation of the length of time of year, month, and day and the relationship between them, and the rules for specifying time series. The official calendar of our country is the Chinese Gregorian Calendar, which is the Gregorian Calendar used in the world . The years of the Chinese Gregorian calendar are divided into normal years and leap years. The normal year is 365 days and the leap year is 366 days. The rules for determining whether a year is a normal year or a leap year are as follows:

 

1.   If the year is a multiple of 4 and not a multiple of 100 , it is a leap year;

2.   If the year is a multiple of 400 , it is a leap year;

3.   Those who do not meet the conditions 1 and 2 are normal years.

 

Summarized in one sentence: one leap for four years, no leap for a hundred years, and then leap for four hundred years.

 

        The rules of the Chinese Gregorian calendar for the month are like this. A year is divided into twelve months. Among them, January, March, May, July, August, October and December are the big months, and there are 31 days in a month. . April, June, September, and November are small months, and there are 30 days in a month . The number of days in February depends on whether it is a leap year. If it is a leap year, February is 29 days, and if it is a normal year, February is 28 days.

        In addition to the year, month and day, people also define another attribute of the date in daily life, which is the day of the week. Weeks are not within the scope of the Gregorian calendar, but people have become accustomed to using weeks to manage and plan time, such as working five days a week, resting two days, etc. The rule of the week has completely changed people’s living habits, so the week has become a calendar. Part of it. The name of the week originated in ancient Babylonian culture. In the 7th and 6th centuries BC, the Babylonians used the week system, and each day of the week was governed by a god. This rule was later spread to ancient Rome and gradually evolved into the current week system.

        How do I know the day of the week? Apart from checking the calendar, is there a way to figure out what day a certain day is? The answer is yes. The week does not have a fixed calendar rule like the year and month, but the calculation of the week also has its own rules. The week is a fixed 7- day cycle with a fixed sequence and does not change with the number of days in leap years, normal years, and large and small months. Therefore, as long as you know exactly which day is the day of the week, you can infer other days as the day of the week. The calculation method is very simple. It is to calculate how many days are the difference between the two dates, and take the remainder of 7 with the number of days of the difference. This remainder is the difference between the week numbers of the two dates. For example, suppose already know 1977 Nian 3 Yue 27 is a Sunday, how that 1978 Nian 3 Yue 27 day is a day of the week? According to the previous method to calculate the 1977 Nian 3 Yue 27 days to 1978 Nian 3 Yue 27 difference between day 365 days, 365 divided by 7 the remainder is 1 , so 1978 Nian 3 YueThe 27th is Monday.

        The key to calculating the day of the week in the above method is to find the number of days between two dates. There are two commonly used methods to calculate the number of days between two dates, one is to use the Gregorian calendar month and year rules to directly calculate, and the other is to use Julian days to calculate. Use the Gregorian calendar rules to directly calculate the number of days between two dates. Simply put, the number of days between the two dates is divided into three parts: the number of days left in the year of the previous date, and the number of days between the two dates The number of days included in the integer year and the number of days past the year of the next date. If the two dates are dates in two adjacent years, the number of days in the second part of the year is 0 . To 1977 in 3 Yue 27 Ri to 2005 in 5 Yue 31 Japan for example, 1977 years remaining number of days is 279 days, and in the middle is an integer from 1978 years to 2005 years (not including 2005 years), a total of 26 years, including 7 leap years and 20 Ge ordinary years, a total of 9862 days, and finally the 2005 years from 1 Yue 1 Ri to 5There are 151 elapsed days on the 31st . The three summarized 10292 days. The algorithm that directly uses the Gregorian calendar rules to calculate the number of days between dates is implemented as follows (in order to simplify the algorithm complexity, this implementation assumes that the date used to locate the week is always before the date that needs to be calculated):

 99 int CalculateDays(int ys, int ms, int ds, int ye, int me, int de)

100 {

101     int days = CalcYearRestDays(ys, ms, ds);

102 

103      if ( ys != ye ) /*not a date in the same year*/

104     {

105         if((ye - ys) >= 2) /*间隔超过一年,要计算间隔的整年时间*/

106         {

107             days += CalcYearsDays(ys + 1, ye);

108         }

109         days += CalcYearPassedDays(ye, me, de);

110     }

111     else

112     {

113         days = days - CalcYearRestDays(ye, me, de);

114     }

115 

116     return days;

117 }

43 /*计算一年中过去的天数,包括指定的这一天*/

44 int CalcYearPassedDays(int year, int month, int day)

45 {

46     int passedDays = 0;

47 

48     int i;

49     for(i = 0; i < month - 1; i++)

50     {

51         passedDays += daysOfMonth[i];

52     }

53 

54     passedDays += day;

55     if((month > 2) && IsLeapYear(year))

56         passedDays++;

57 

58     return passedDays;

59 }

60 

61 /*计算一年中还剩下的天数,不包括指定的这一天*/

62 int CalcYearRestDays(int year, int month, int day)

63 {

64     int leftDays = daysOfMonth[month - 1] - day;

65 

66     int i;

67     for(i = month; i < MONTHES_FOR_YEAR; i++)

68     {

69         leftDays += daysOfMonth[i];

70     }

71 

72     if((month <= 2) && IsLeapYear(year))

73         leftDays++;

74 

75     return leftDays;

76 }

77 

78 /*

79 计算years年1月1日和yeare年1月1日之间的天数,

80 包括years年1月1日,但是不包括yeare年1月1日

81 */

82 int CalcYearsDays(int years, int yeare)

83 {

84     int days = 0;

85 

86     int i;

87     for(i = years; i < yeare; i++)

88     {

89         if(IsLeapYear(i))

90             days += DAYS_OF_LEAP_YEAR;

91         else

92             days += DAYS_OF_NORMAL_YEAR;

93     }

94 

95     return days;

96 }

 

        另一种计算两个日期相差天数的方法是利用儒略日(Julian DayJD)进行计算。首先介绍一下儒略日,儒略日是一种不记年,不记月,只记日的历法,是由法国学者Joseph Justus Scaliger15401609)在1583年提出来的一种以天数为计量单位的流水日历。儒略日和儒略历(Julian Calendar)没有任何关系,命名为儒略日也仅仅他本人为了纪念他的父亲――意大利学者Julius Caesar Scaliger14841558)。简单来讲,儒略日就是指从公元前471311UTC 12:00开始所经过的天数,JD0就被指定为公元前471311 12:00到公元前47131212:00之间的24小时,依次顺推,每一天都被赋予一个唯一的数字。例如从19961112:00开始的一天就是儒略日JD2450084。使用儒略日可以把不同历法的年表统一起来,很方便地在各种历法中追溯日期。如果计算两个日期之间的天数,利用儒略日计算也很方便,先计算出两个日期的儒略日数,然后直接相减就可以得到两个日期相隔的天数。

        由公历的日期计算出儒略日数是一个很简单的事情,有多个公式可以计算儒略日,本文选择如下公式计算儒略日:

其中y是年份,m是月份,d是日期,如果m小于或等于2,则m修正为m+12,同时年份修正为y-1c值由以下方法计算:

下面就是由公历日期计算儒略日的算法实现:

119 int CalculateJulianDay(int year, int month, int day)

120 {

121     int B = 0;

122 

123     if(month <= 2)

124     {

125         month += 12;

126         year -= 1;

127     }

128     if(IsGregorianDays(year, month, day))

129     {

130         B = year / 100;

131         B = 2 - B + year / 400;

132     }

133 

134     double dd = day + 0.5000115740; /*本日12:00后才是儒略日的开始(过一秒钟)*/

135     return int(365.25 * (year + 4716) + 0.01) + int(30.60001 * (month + 1)) + dd + B - 1524.5;

136 }

 

        儒略日的计算通常精确到秒,得到的JD数也是一个浮点数,本文仅仅是为了计算日期相隔的整数天数,因此都采用整数计算。由于儒略日的周期开始与每天中午12:00,而历法中的天数通常是从0:00开始的,因此儒略日计算上对日期的天数进行了修正。1977327日的儒略日是24432302005531日的儒略日是2453522,差值是10292,和前一种方法计算的结果一致。

        我们用两种方法计算出两个日期之间的天数都是10292,现在用10292除以7得到余数是2,也就是说2005531日与1977327日星期数差两天,所以2005531日就是是星期二。

        上述计算星期的方法虽然步骤简单,但是每次都要计算两个日期的时间差,不是非常方便。如果能够有一个公式可以直接根据日期计算出对应的星期岂不是更好?幸运的是,这样的公式是存在的,下篇将继续介绍公式法直接计算某一天星期数的算法。

 

 

 

小知识1:公历的闰年

中国公历(也就是格里历)的置闰规则是四年一闰,百年不闰,四百年再闰,为什么会有这么奇怪的置闰规则呢?这实际上与天体运行周期与人类定义的历法周期之间的误差有关。地球绕太阳运转的周期是365.2422天,即一个回归年(Tropical Year),而公历的一年是365天,这样一年就比回归年短了0.2422日,四年积累下来就多出0.9688天(约1天),于是设置一个闰年,这一年多一天。这样一来,四个公历年又比四个回归年多了0.0312天,平均每年多0.0078天,这样经过四百年就会多出3.12天,也就是说每四百年要减少3个闰年才行,于是就设置了百年不闰,四百年再闰的置闰规则。

实际上公历的置闰还有一条规则,就是对于数值很大的年份,如果能整除3200,同时能整除172800则是闰年。这是因为前面即使四百年一闰,仍然多了0.12天,平均就是每天多0.0003天,于是每3200年就又多出0.96天,也就是说每3200年还要减少一个闰年,于是能被3200整除的年就不是闰年了。然而误差并没有终结,每3200年减少一个闰年(减少一天)实际上多减了0.04天,这个误差还要继续累计计算,这已经超出了本文的范围,有兴趣的读者可以自己计算。

 

Guess you like

Origin blog.csdn.net/orbit/article/details/7749723