输入一个年份然后打印日历,从书中修正得到
下面程序因为没有用蔡勒公式,选择了历史上已知的星期几的某一天进行计算。
#include <stdio.h>
/*
表示一周中的日子,对它进行了编号;这样编号有好处。
倘若你知道某天是星期几可以用(weekday(0~6) + k)% 7求出该天是星期几,其中k表示第几天;
例如:(5 + 10) % 7表示今天是星期五,今天后第十天是星期一;
还可以求出本周的下一天是星期几,可移动到那一天不会出现weekday++带来的困扰;
weekday = (weekday + 1) % 7;
*/
#define Sunday 0
#define Monday 1
#define Tuesday 2
#define Wednesday 3
#define Thursday 4
#define Friday 5
#define Saturday 6
/*函数声明*/
void GiveInstrutions(void); //提示函数;
int GetyearFromuser(void); //输入年份,打印日历;
void PrintCalendar(int year); //一年的1~12个月;
void PrintCalendarMonth(int month, int year); //显示某个月的日历;
void IndentFirstLine(int weekday); //输出日历的第一行,使第一天出现在正确的位置;
int Monthdays(int month, int year); //计算某月的天数;
int FirstDayofMonth(int month, int year); //计算某月的第一天是星期几;
char *MonthName(int month); //打印出该月的名字;
bool IsLeapyear(int year); //判断是不是闰年;
int main()
{
int year;
GiveInstrutions();
year = GetyearFromuser();
PrintCalendar(year);
return 0;
}
/*提示说明函数*/
void GiveInstrutions(void)
{
printf("This program displays a calendar for a full\n");
printf("year. The year must not be for 1900.\n");
}
/*输入年份*/
int GetyearFromuser(void)
{
int year;
while(true)
{
printf("Which year?\n");
scanf("%d", &year);
if (year >= 1900) return (year); //因为后面是以已知1900年1月1日是星期一作为计算的起点的;
printf("The year must be at least 1900.\n");
}
}
/*打印函数*/
void PrintCalendar(int year)
{
int month;
printf("\n");
for (month=1; month<=12; month++) //该年的1~12月;
{
PrintCalendarMonth(month, year);//调用打印月的函数;要知道该月有多少天,第一天是星期几;
printf("\n"); //每个月之间要换行;
}
}
/*打印月的函数*/
void PrintCalendarMonth(int month, int year)
{
int weekday, nDays, day;
printf(" %s %d\n", MonthName(month), year);
printf(" Su Mo Tu We Th Fr Sa\n");
nDays = Monthdays(month, year); //计算月的天数;
weekday = FirstDayofMonth(month, year); //计算该月的第一天是星期几;
IndentFirstLine(weekday); //打印空格,确保第一天出现在正确的位置;
for (day=1; day<=nDays; day++) //假设保存了星期几的记录,则用下面的循环打印;所以还要知道该月的天数、第一天是星期几、确保第一天在正确的位置;
{
printf(" %2d", day);
if (weekday == Saturday) //星期六之后换行;
printf("\n");
weekday = (weekday + 1) % 7;
}
if (weekday != Sunday) printf("\n"); //即使这个星期不是在星期日结束,最后一行也有换行符;换成星期六也行;
}
/*确保第一天出现在正确的位置*/
void IndentFirstLine(int weekday)
{
int i;
for (i=0; i<weekday; i++)
printf(" "); //在前面打印空格;
}
/*计算该月的天数*/
int Monthdays(int month, int year) //因为闰年和平年的天数不一样,所以还要有一个判断闰年与否的函数;
{
switch(month)
{
case 2:
if (IsLeapyear(year)) return (29);
return (28);
case 4: case 6: case 9: case 11:
return (30);
default :
return (31);
}
}
/*这个月的第一天是星期几*/
int FirstDayofMonth(int month, int year)
{
int weekday, i;
weekday = Monday; //因为1900年1月1日是星期一;
for (i=1900; i<year; i++)
{
weekday = (weekday + 365) % 7;
if (IsLeapyear(i))
weekday = (weekday + 1) % 7;
}
/*此处计算出的是当年1月1日是星期几; 因为weekday = (weekday + k)% 7 计算的是从那天后的第k天,所以天数刚好多一*/
//printf("%d\n", weekday);
for (i=1; i<month; i++)
{
int sum = 0;
sum += Monthdays(i, year);
weekday = (weekday + sum) % 7; //某月1号,所以要加上该年前几个月的天数;这里最重要;
}
return (weekday);
}
/*
这里采用了一个简单的方法,取历史上的已知星期几的某一天;
从那天开始算按闰年与否每年加365/366天;
对于要算当年的要处理的月份之前的月,加上这月的天数;
运用weekday = (weekday + k) % 7。
*/
/*该月份的名字*/
char *MonthName(int month)
{
switch(month)
{
case 1: return ("January");
case 2: return ("Fberuary");
case 3: return ("March");
case 4: return ("April");
case 5: return ("May");
case 6: return ("June");
case 7: return ("July");
case 8: return ("August");
case 9: return ("September");
case 10: return ("October");
case 11: return ("November");
case 12: return ("December");
default: return ("Illegal month");
}
}
/*判断闰年与否*/
bool IsLeapyear(int year)
{
return (((year % 4 == 0) && (year % 100 != 0)) || (year % 400 == 0));
}
/*仅供参考,已经测试,修正于C语言的编程与艺术的译本*/