万年历算法的实现

更新时间:2024-05-26 20:06:01 阅读量: 综合文库 文档下载

说明:文章内容仅供预览,部分内容可能不全。下载后的文档,内容与下面显示的完全一致。下载之前请确认下面内容是否您想要的,是否完整无缺。

/*

* cal.c *

* Created on: Apr 10, 2011 * Author: Administrator *

* 现行的格里历是从儒略历演化而来的。儒略历每4年一个润年,润年366天,平年365天。

* 如果从公元1年算的话,那么凡是能够被4整除的都是润年。从天文角度看,儒略历这种

* 历法是有误差的,到16世纪误差已经达到了10天。1582年,罗马教皇对儒略历进行了

* 一次校定,该年的10-5到10-14这10天被抹掉,并规定凡不能被400整除的世纪年不再

* 算为润年,校定之后的儒略历即为现行的格里历。 *

* 但是英国直到1752年才开始使用格里历,此时时间误差已经达到了11天,原因是1700

* 年在儒略历中是润年,而在格里历中是平年。1752年英国议会决定将本年的9-3到9-13

* 这11天抹掉,以同步格里历。当时美国不是英国的殖民地,所以在美国的日历中也是没有

* 1752-9-3到1752-9-13这11天的。 我们知道UNIX系统源于美国,Linux系统源于UNIX,

* 这就是为什么当我们在Linux系统中敲\这条命令时,会看到一个只有19天

* 的9月。 *

* 以上内容参考自维基百科 *

* 本程序模似Linux中cal命令的部分功能。允许输入的年分为1...9999。 * 编译器:gcc V4.5.0 *

* 分析:

* 1752年是特殊年,1752-9月是特殊年中的特殊月。 *

* 1752-9-2之前,采用的是儒略历,凡能被4整除都为润年;1752-9-14之后,采用的是

* 格里历,不能被400整除的世纪年不再作为润年。 *

* 格里历闰年计算方法 * 世纪年 ***4000的倍数 **3200/1900的倍数 400的倍数 100的倍数 4的倍数

* 结果 不闰 不闰 闰 不闰 闰 * 注:

* 3200/1900的倍数是天文科学家研究出来

* 增加4000的倍数不闰更准确,但目前只是世纪年还不足4000,因此尚未用途 *

* 格里历在400年的周期中有146,097天,恰好是20,871 星期。所以,例如,格里历七年、

* 407年、807年、1207年、1607年、2007年的日期与星期是完全相同的。也就是说,格里

* 历每400年一个周期。 *

* 公元1-1-1,按儒略历是周六,按格里历是周一;因1752-9-2之前采用的都是儒略历,所以

* 公元1-1-1应该是周六。 * 1752-9-14是周四。 *

* 公元1-1-1到1752-9-2,日期是连续的,儒略历;公元1752-9-14至今,日期是连续的,格里历。 *

* 对于给定的一个 年(Y)-月(M)-日(D) ,则从公元1-1-1到 给定的日期的天数为:

* 1752-9-2前:365*(Y-1)+(Y-1)/4+e,其中e表示从Y-1-1到Y-M-D的天数,

* 且这天是该周的第((Y-1)+(Y-1)/4+5+e)%7天。 *

* 1752-9-14后:365*(Y-1)+(Y-1)/4-(Y-1)/100+(Y-1)/400+e, * 且这天是该周的第((Y-1)+(Y-1)/4-(Y-1)/100+(Y-1)/400+e)%7天。

*

* 需要注意的是,从1-1-1到1752-9-14之后的某个日期的天数并非实际的天数,它只是根据格里历的

* 的规则推算出来的,目的也只是为了得到日期与星期的对应。 *

* 算法思想:

* 对于年历,若能知道本年的1月1日是周几,也就知道了本年的年历分布。 * 对于月历,若能知道本月的1日是周几,也就知道了本月的月历分布。 * 所以关键是正确的推算出给定的一个日期是星期几。以下为推算方法: * 1752-9-2之前:((Y-1)+(Y-1)/4+5+e)%7

* 1752-9-14之后:((Y-1)+(Y-1)/4-(Y-1)/100+(Y-1)/400+e)%7 * 把1800-2199年当成一个周期,对于其后的日期,只需在1800-2199之间找到

* 对应的日期,就可以知道是星期几,这样就不用再考虑4000年之后4000的倍数

* 不是润年这种情况。 (Y-1800)@0+1800 * *

* */

#include #include #include void PrintHelp(); int* CurrentCal();

int CheckYear(int year); int CheckMonth(int month);

int CheckDay(int year, int month, int day); int IsNum(char *argv);

int IsLegalParameter(int argc, char **argv); long StrToLong(char *argv);

long Power(int baseNumber, int exponent); void PrintCalendar(int year);

void PrintCommonCalendar(int year, int row); void PrintOneQuarter(int year, int mfOfWeek, int mfDays, int msOfWeek, int msDays, int mtOfWeek, int mtDays, int row); void Print1752_3Quarter();

void PrintMonthlyCalendar(int year, int month); void PrintMonthName(char *, int year);

void PrintCommonMonthlyCalendar(int dayOfWeek, int days); void PrintDayOfWeek(int year, int month, int day); int DayOfWeek(int year, int month, int day); int CurrentDays(int year, int month, int day);

int a;//输入的年这个参数的字符串长度,主根用于后面的PrintMonthName(char *, int)函数

int main(int argc, char **argv) { int year, month, day;

int flag = IsLegalParameter(argc, argv); switch (flag) { case -1:

printf(\); PrintHelp(); break; case 0:

PrintMonthlyCalendar(*(CurrentCal()), *(CurrentCal() + 1)); break; case 1: {

a = strlen(*(argv + 1)); switch (argc) { case 2:

year = StrToLong(*(argv + 1)); if (!CheckYear(year)) { break; }

printf(\, year); PrintCalendar(year); break; case 3:

year = StrToLong(*(argv + 1)); if (!CheckYear(year)) { break;

}

month = StrToLong(*(argv + 2)); month = StrToLong(*(argv + 2)); if (!CheckMonth(month)) { break; }

PrintMonthlyCalendar(year, month); break; case 4:

year = StrToLong(*(argv + 1)); if (!CheckYear(year)) { break; }

month = StrToLong(*(argv + 2)); if (!CheckMonth(month)) { break; }

day = StrToLong(*(argv + 3));

if (!CheckDay(year, month, day)) { break; }

PrintDayOfWeek(year, month, day); break; default:

year = StrToLong(*(argv + 1)); if (!CheckYear(year)) { break; }

month = StrToLong(*(argv + 2)); if (!CheckMonth(month)) { break; }

day = StrToLong(*(argv + 3));

if (!CheckDay(year, month, day)) { break; }

PrintDayOfWeek(year, month, day); break; } }

default: break; }

return 0; }

void PrintHelp() {

printf(\:cal [ [ []]\); }

int CheckYear(int year) {

if (year == 0 || year > 9999) {

printf(\, year); return 0; }

return 1; }

int CheckMonth(int month) {

if (month < 1 || month > 12) {

printf(\, month); return 0; }

return 1; }

int CheckDay(int year, int month, int day) { switch (month) { case 1:

if (day < 1 || day > 31) {

printf(\, day); return 0; }

return 1; break; case 2:

if ((year < 1753 && year % 4 == 0) || ((year % 4 == 0 && year % 100

!= 0) || year % 400 == 0)) { if (day < 1 || day > 29) {

printf(\, day); return 0; } }

if (day < 1 || day > 28) {

printf(\, day); return 0; }

return 1; break; case 3:

if (day < 1 || day > 31) {

printf(\, day); return 0; }

return 1; break; case 4:

if (day < 1 || day > 30) {

printf(\, day); return 0; }

return 1; break; case 5:

if (day < 1 || day > 31) {

printf(\, day); return 0; }

return 1; break; case 6:

if (day < 1 || day > 30) {

printf(\, day); return 0; }

return 1; break; case 7:

if (day < 1 || day > 31) {

printf(\ return 0; }

return 1; break; case 8:

if (day < 1 || day > 31) {

printf(\ return 0; }

return 1; break; case 9:

if (day < 1 || day > 30) {

printf(\ return 0; }

return 1; break; case 10:

if (day < 1 || day > 31) {

printf(\ return 0; }

return 1; break; case 11:

if (day < 1 || day > 30) {

printf(\ return 0; }

return 1; break; case 12:

if (day < 1 || day > 31) {

printf(\ return 0;

, day); , day); , day); , day); , day); , day); }

return 1; break; default: break; return 0; } } /**

* 以下函数返回一个包含当前年 月 日的整型数组 */

int* CurrentCal() { time_t nowtime;

struct tm *timeinfo; time(&nowtime);

timeinfo = localtime(&nowtime); int cal[3];

cal[0] = timeinfo->tm_year + 1900; cal[1] = timeinfo->tm_mon + 1; cal[2] = timeinfo->tm_mday; return cal; } /**

* 以下函数打印年历,以月为单位,分成四行打印,每行打印三个月 */

void PrintCalendar(int year) { int i;

for (i = 0; i < 4; ++i) { switch (i) { case 0:

//打印第1行,1-3月的月历

printf(\March\\n\);

printf(

\--------------------\\n\); printf(

\Mo Tu We Th Fr Sa Su Mo Tu We Th Fr Sa Su Mo Tu We Th Fr Sa\\n\);

PrintCommonCalendar(year, 1); break; case 1:

//打印第2行,4-6月的月历

printf(\June\\n\);

printf(

\--------------------\\n\); printf(

\Mo Tu We Th Fr Sa Su Mo Tu We Th Fr Sa Su Mo Tu We Th Fr Sa\\n\);

PrintCommonCalendar(year, 2); break; case 2:

//打印第3行,7-9月的月历 printf(

\September\\n\); printf(

\--------------------\\n\); printf(

\Mo Tu We Th Fr Sa Su Mo Tu We Th Fr Sa Su Mo Tu We Th Fr Sa\\n\);

PrintCommonCalendar(year, 3); break; case 3:

//打印第4行,11-12月的月历 printf(

\December\\n\);

printf(

\--------------------\\n\); printf(

\Mo Tu We Th Fr Sa Su Mo Tu We Th Fr Sa Su Mo Tu We Th Fr Sa\\n\);

PrintCommonCalendar(year, 4); break; default: break; } } } /**

* 打印出给定年,给定行的3个月的月历。 */

void PrintCommonCalendar(int year, int row) {

int mfOfWeek, mfDays; //mfOfWeek表示该行第1个月的1号是周几,mfDays表示第1个月的天数

int msOfWeek, msDays; //mfOfWeek表示该行第2个月的1号是周几,mfDays表示第2个月的天数

int mtOfWeek, mtDays; //mfOfWeek表示该行第3个月的1号是周几,mfDays表示第3个月的天数 switch (row) { case 1:

mfOfWeek = DayOfWeek(year, 1, 1); mfDays = 31;

msOfWeek = DayOfWeek(year, 2, 1); msDays = 28;

mtOfWeek = DayOfWeek(year, 3, 1); mtDays = 31;

PrintOneQuarter(year, mfOfWeek, mfDays, msOfWeek, msDays, mtOfWeek,

mtDays, row); break; case 2:

mfOfWeek = DayOfWeek(year, 4, 1); mfDays = 30;

msOfWeek = DayOfWeek(year, 5, 1); msDays = 31;

mtOfWeek = DayOfWeek(year, 6, 1); mtDays = 30;

PrintOneQuarter(year, mfOfWeek, mfDays, msOfWeek, msDays, mtOfWeek,

mtDays, row); break; case 3:

if (year == 1752) {

Print1752_3Quarter(); break; }

mfOfWeek = DayOfWeek(year, 7, 1); mfDays = 31;

msOfWeek = DayOfWeek(year, 8, 1); msDays = 31;

mtOfWeek = DayOfWeek(year, 9, 1); mtDays = 30;

PrintOneQuarter(year, mfOfWeek, mfDays, msOfWeek, msDays, mtOfWeek,

mtDays, row); break; case 4:

mfOfWeek = DayOfWeek(year, 10, 1); mfDays = 31;

msOfWeek = DayOfWeek(year, 11, 1); msDays = 30;

mtOfWeek = DayOfWeek(year, 12, 1); mtDays = 31;

PrintOneQuarter(year, mfOfWeek, mfDays, msOfWeek, msDays, mtOfWeek,

mtDays, row); break; default: break; } } /**

* 以下函数打印一个季度,也即是在同一行上的3个月的月历。 *

* 打印方式肯定是从上到下,从左到右依次打印。 *

* 每月至多有6周,至少有5周;第1周、第5周和第6周(如果有第6周)是需要特殊处理 * 的三个周,因为这3周并不一定总是有7天。 */ void PrintOneQuarter(int year, int mfOfWeek, int mfDays, int msOfWeek, int msDays, int mtOfWeek, int mtDays, int row) {

int mfDay = 1, msDay = 1, mtDay = 1; //对已经打印的天数记数 int i, j;

char space = 32;

//如果要打印的是1-3月的月历,则应该判断一下该年的2月是29天不是29天 if (row == 1) {

if ((year < 1753 && year % 4 == 0) || (year >= 1753 && ((year % 4 == 0

&& year % 100 != 0) || year % 400 == 0))) { msDays = 29; } }

for (i = 0; i < 6; ++i) { if (i == 0) {

//输出第一个月的第一周 if (mfOfWeek > 0) {

printf(\, 3 * mfOfWeek, space); }

for (j = mfOfWeek; j < 7; ++j) { printf(\, mfDay); mfDay++; }

//输出第二个月的第一周

printf(\, 3 * msOfWeek + 1, space); for (j = msOfWeek; j < 7; ++j) { printf(\, msDay); msDay++; }

//输出第三个月的第一周

printf(\, 3 * mtOfWeek + 1, space); for (j = mtOfWeek; j < 7; ++j) { printf(\, mtDay); mtDay++; }

continue; }

//打印2-4周 if (i < 4) { printf(

\

2d - - - - - -\,

mfDay, mfDay + 1, mfDay + 2, mfDay + 3, mfDay + 4, mfDay

+ 5, mfDay + 6, msDay, msDay + 1, msDay + 2, msDay

+ 3, msDay + 4, msDay + 5, msDay + 6, mtDay, mtDay

+ 1, mtDay + 2, mtDay + 3, mtDay + 4, mtDay + 5,

mtDay + 6); mfDay += 7; msDay += 7; mtDay += 7; continue; }

//打印第1个月的第5周或第6周

if (mfDays - mfDay > 6) { //未打印天数不少于7天时,能够占满1周,直接打印

printf(\- - - - - - \, mfDay, mfDay + 1, mfDay

+ 2, mfDay + 3, mfDay + 4, mfDay + 5, mfDay + 6); mfDay += 7;

} else { //未打印天数不足一周时,按以下方式打印 printf(\);

for (j = 0; j < 7; ++j) {

//有数据打印数据,没有数据用空格填充 if (mfDay <= mfDays) {

printf(\, mfDay++); } else {

printf(\); } } }

printf(\);

//打印第2个月的第5周或第6周 if (msDays - msDay > 6) {

printf(\, msDay, msDay + 1, msDay + 2,

msDay + 3, msDay + 4, msDay + 5, msDay + 6); msDay += 7; } else {

for (j = 0; j < 7; ++j) { if (msDay <= msDays) {

printf(\, msDay++); } else {

printf(\); } } }

printf(\);

//打印第3个月的第5周或第6周 if (mtDays - mtDay > 6) {

printf(\, mtDay, mtDay + 1, mtDay + 2,

mtDay + 3, mtDay + 4, mtDay + 5, mtDay + 6); mtDay += 7; } else {

for (j = 0; j < 7; ++j) { if (mtDay <= mtDays) {

printf(\, mtDay++); } } } }

printf(\); } /**

*特殊的1752年第3季度:7月8月9月 */

void Print1752_3Quarter() { char space = 32;

printf(\, space, space, space);

printf(\5 6 7 8 9 10 11 2 3 4 5 6 7 8 17 18 19 20 21 22 23\\n\);

printf(\29 30\\n\);

printf(\); printf(\); printf(\, space); } /**

* 打印指定月的月历 */

void PrintMonthlyCalendar(int year, int month) { int dayOfWeek = DayOfWeek(year, month, 1); switch (month) { case 1:

PrintMonthName(\, year);

PrintCommonMonthlyCalendar(dayOfWeek, 31); break; case 2:

PrintMonthName(\, year); if (year < 1753 && year % 4 == 0) {

PrintCommonMonthlyCalendar(dayOfWeek, 29); break;

} else if ((year % 4 == 0 && year % 100 != 0) || year % 400 ==

0) {

PrintCommonMonthlyCalendar(dayOfWeek, 29); break; }

PrintCommonMonthlyCalendar(dayOfWeek, 28); break; case 3:

PrintMonthName(\, year);

PrintCommonMonthlyCalendar(dayOfWeek, 31); break; case 4:

PrintMonthName(\, year);

PrintCommonMonthlyCalendar(dayOfWeek, 30); break; case 5:

PrintMonthName(\, year);

PrintCommonMonthlyCalendar(dayOfWeek, 31); break; case 6:

PrintMonthName(\, year);

PrintCommonMonthlyCalendar(dayOfWeek, 30); break; case 7:

PrintMonthName(\, year);

PrintCommonMonthlyCalendar(dayOfWeek, 31); break; case 8:

PrintMonthName(\, year);

PrintCommonMonthlyCalendar(dayOfWeek, 31); break; case 9:

PrintMonthName(\, year); if (year == 1752) { //特殊的1752-9 printf(

\20 21 22 23\\n24 25 26 27 28 29 30\); break; }

PrintCommonMonthlyCalendar(dayOfWeek, 30); break; case 10:

PrintMonthName(\, year);

PrintCommonMonthlyCalendar(dayOfWeek, 31); break; case 11:

PrintMonthName(\, year);

PrintCommonMonthlyCalendar(dayOfWeek, 30); break; case 12:

PrintMonthName(\, year);

PrintCommonMonthlyCalendar(dayOfWeek, 31); break; default: break; } } /**

* 以下函数的作用仅仅是将月名在水平方向上打印到月历的中间 */

void PrintMonthName(char *monthName, int year) { char space = 32; int preSpaces = 0;

preSpaces = (21 - strlen(monthName) - a - 1) / 2;

printf(\, preSpaces, space, monthName, year); printf(\); } /**

* 根据一个月的天数及该月有几天这两个参数,打印出该月的月历 */

void PrintCommonMonthlyCalendar(int dayOfWeek, int days) { int i, j, day = 1; char space = 32;

int weeks = (days + dayOfWeek) / 7 + 1; //计算出该月有几周 printf(\); printf(\, 3 * dayOfWeek, space); for (i = 0; i < weeks; ++i) { if (i == 0) { //打印第1周

for (j = dayOfWeek; j < 7; ++j) { printf(\, day); day++; }

continue; }

//打印第2周到倒数第2周 if (i < weeks - 1) {

printf(\, day, day + 1, day + 2, day

+ 3, day + 4, day + 5, day + 6); day += 7; continue; }

//打印最后一周 printf(\);

for (j = 0; j < 7; ++j) { if (day <= days) {

printf(\, day++); } }

printf(\); } } /**

* 打印给定的年、月、日是周几 */

void PrintDayOfWeek(int year, int month, int day) {

char *names[9] = { \, \, \, \, \,

\, \ };

int dayOfWeek = DayOfWeek(year, month, day); if (dayOfWeek == -1) { return; }

printf(\%s\\n\, year, month, day, *(names + dayOfWeek)); } /**

* 以下函数返回给定的年、月、日是周几 */

int DayOfWeek(int year, int month, int day) {

if ((year < 1752) || (year == 1752 && month < 9) || (year == 1752 && month

== 9 && day < 3)) {

return ((year - 1) + (year - 1) / 4 + CurrentDays(year, month, day) + 5)

% 7; }

if (year == 1752 && month == 9 && (day > 2 && day < 14)) { printf(\本万年历中没有%d-%d-%d这一天\\n\, year, month, day); return -1; }

if (year > 2199) {

year = (year - 1800) % 400 + 1800; }

return ((year - 1) + (year - 1) / 4 - (year - 1) / 100 + (year - 1) / 400

+ CurrentDays(year, month, day)) % 7; } /**

* 以下函数计算从给定年的1-1到month-day的天数 */

int CurrentDays(int year, int month, int day) {

int days = 0; switch (month) { case 1:

days = day; break; case 2:

days = 31 + day; break; case 3:

days = 59 + day; break; case 4:

days = 90 + day; break; case 5:

days = 120 + day; break; case 6:

days = 151 + day; break; case 7:

days = 181 + day; break; case 8:

days = 212 + day; break; case 9:

days = 243 + day; break; case 10:

days = 273 + day; break; case 11:

days = 304 + day; break; case 12:

days = 334 + day; break; default: break; }

if (year < 1753 && year % 4 == 0) { if (month > 2) { ++days; }

} else if ((year % 4 == 0 && year % 100 != 0) || year % 400 == 0) { if (month > 2) { ++days; }

}

return days; } /*

* 以下函数判断一个字符串是否能被转换成数字 */

int IsNum(char *argv) { while (*argv != '\\0') {

if (*argv >= 48 && *argv <= 57) { argv++; continue; }

return 0; }

return 1; } /*

*以下函数判断输入的参数是否合法 */

int IsLegalParameter(int argc, char **argv) { if (argc == 1) {

return 0; //0表示没有输入参数 }

while (argc > 1) {

if (!IsNum(*(argv + --argc))) {

return -1; //-1表示输入的参数不合法 } }

return 1; //1表示输入了参数,并且参数是合法的 } /**

* 以下函数将一个字符串转换成数字。 */

long StrToLong(char *argv) { if (!IsNum(argv)) { return 0; }

long result = 0;

int argvLength = strlen(argv); int nums[argvLength - 1]; int *pInt = nums;

do {

*(pInt++) = *argv - 48; argv++;

} while (*argv != '\\0');

pInt = nums;

do {

result += *pInt * Power(10, argvLength - 1); pInt++;

argvLength--;

} while (argvLength != 0); return result; } /*

* According to the base number and the exponent calculate power. * 根据基数和指数,计算出乘方数。 */

long Power(int baseNumber, int exponent) { long result = 1;

if (exponent == 0) { return 1; } do {

result *= baseNumber; exponent--;

} while (exponent != 0); return result; }

本文来源:https://www.bwwdw.com/article/clp7.html

Top