万年历(c语言)

一、项目背景

​ 万年历是模仿生活中挂历,以电子的形式实现日历的基本功能。可以输出公元元年(即公元1年)1月1日以后任意月份的月历,以及查询指定日期,查看全年日历等等。

二、设计目的

  • 如何根据所给的日期,计算出对应星期
  • 如何按合适的方式打印日历
  • 如何获取系统时间
  • 如何进行光标定位

三、项目功能需求

1、获取当前时间

  • 获取系统时间作为默认值,显示系统日期所在月份的月历。

2、日期有效性检查

  • 对日期进行检查,若发现日期无意义或者不符合实际,将拒绝该功能执行,并显示错误提示。

3、日期查询

  • 输入指定日期,查询后显示日期所在月份的月历,并突出显示日期。

4、日期调整

  • 通过键盘输入来选取对应功能,可以增减年份、月份和日期,并能将所选日期重置为系统时间。

5、显示全年日历

  • 键入对应功能键后输出当前所在年份的全年日历。

四、系统的功能结构图

在这里插入图片描述

五、功能模块介绍

1、时间获取模块

  • 用于获取系统当前时间,在主函数中实现,用一个时间结构体得到并存储具体时间。

2、排版输出模块

  • 优化界面,通过自定义的GotoXY函数来改变光标位置,打印指定数量的空格,打印分割线。

3、功能控制模块

  • 进行闰年判断,返回指定日期对应的星期,日期有效性检查

4、日历显示模块

  • 设计日历的生成和显示,输出用户指定的日期的对应信息如星期几,所在月份。

5、功能选择模块

  • 通过键盘输入对应的键选取所要执行的功能,以调整日期,重置日期等等。

六、详细设计

1、主函数

首先通过时间结构体获取系统时间,作为程序的默认时间。然后是调用函数输出提示信息并进去等待输入状态。

2、排版输出模块

功能设计

  • 该模块主要用于排版,通过改变光标位置而改变输出内容的位置,以及打印空格、下划线等使界面更清晰美观。

3、功能控制模块

功能设计

  • 模块主要用于判断传入的年份是否是闰年,进行传入日期有效性检查以及返回传入日期是星期几。

4、日历显示模块

功能设计

  • 模块用于输出当前选择日期所在月份对应的月历以及所指定日期和系统日期的信息,如星期几,日期等,并打印功能说明模块。

日历显示模块流程图

在这里插入图片描述

5、功能选择模块

功能设计

  • 模块主要用于响应键盘操作,依据获取的按键值选择相应的功能来响应,从而实现了功能选择。该函数主要由GetKey()实现。

七、代码

/*    1.程序预处理    */

/*    头文件加载    */
#include<stdio.h>
#include<windows.h>
#include<time.h>
#include<conio.h>

/*    定义符号常量    */
#define LAYOUT 45
#define LINE_NUM 30
#define UP 0x48
#define DOWN 0x50
#define LEFT 0x4b
#define RIGHT 0x4d
#define PAGE_UP 0x49
#define PAGE_DOWN 0x51

/*    全局变量    */
struct Date{
	int iYear;
	int iMonth;
	int iDay;
};
struct Date stSystemDate, stCurrentDate;
int iNumCurrentMon = 0;
int iNumLastMon = 0;
int aiMon[13] = { 0, 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 };
char acMon[13][10] = { "\0", "January", "February", "March", "April", "May", "June",
"July", "Aguest", "September", "October", "November", "December" };

/*    自定义函数声明    */
int GetWeekday(int iYear, int iMonth, int iDay);
int IsLeapYear(int iYear);
void GotoXY(int x, int y);
void CheckDate();
void GetKey();
void PrintSpace(int n);
void PrintUnderline();
void PrintInstruction();
void PrintWeek(struct Date *pstTempDate);
void PrintCalendar(int iYear, int iMonth, int iDay);
void PrintWholeYear(int iYear, int iMonth, int iDay);

/*    2.功能控制程序    */

/*    检查日期有效性    */

void CheckDate()
{
	if (stCurrentDate.iYear <= 0)
	{
		GotoXY(0, 22);
		printf("The year should be a positive number!\n");


		GotoXY(0, 23);
		printf("Press any key to continue......");
		getch();         
		/*  重置为系统的当前时间  */
		stCurrentDate = stSystemDate;
	}

	/*  检查月份是否有效  */
	if (stCurrentDate.iMonth < 1 || stCurrentDate.iMonth>12)
	{
		GotoXY(0, 22);
		printf("The month(%d) is invalid!\n", stCurrentDate.iMonth);

		GotoXY(0, 23);
		printf("Press any key to continue......");
		getch();

		stCurrentDate = stSystemDate;
	}
}

/*  判断是否为闰年  */
int IsLeapYear(int iYear)
{
	if (iYear <= 0) 			/*  检查年份是否大于0  */
	{
		printf("The year should be a positive number!\n");
		return -1;
	}
	if (iYear % 4 == 0 && iYear % 100 || iYear % 400 == 0) /*  依据年份判断是否为闰年  */
		return 1;
	else
		return 0;
}
/*  定位到第y行 第x列  */
void GotoXY(int x, int y)
{
	HANDLE hOutput = GetStdHandle(STD_OUTPUT_HANDLE);
	COORD loc;
	loc.X = x;
	loc.Y = y;
	SetConsoleCursorPosition(hOutput, loc);
	return;
}

/*  根据给定日期计算星期函数  */
int GetWeekday(int iYear, int iMonth, int iDay)
{
	int iWeekday = 0, i, iSum = 0;

	if (IsLeapYear(iYear)) /*  是闰年就返回1 否则返回0  */
		aiMon[2] = 29;
	else
		aiMon[2] = 28;
	for (i = 1; i < iMonth; i++)
	{
		iSum += aiMon[i];
	}
	iSum += iDay; /* 该日期到本年1月1日之前的天数 */
	iWeekday = ((iYear - 1) * 365 + (iYear - 1) / 4 - (iYear - 1) / 100 + (iYear - 1) / 400 + iSum) % 7;
	return iWeekday;
}

/*    3.打印输出    */

void PrintSpace(int n)
{
	if (n<0)
	{
		printf("It shouldn't be a negative number!\n");
		return;
	}
	while (n--)
		printf(" ");
}

void PrintUnderline()
{
	int i = LINE_NUM;
	while (i--)
		printf("-");
}

/*  输出用法说明  */
void PrintInstruction()
{
	GotoXY(0, 0);
	printf("==========Instruction==========");

	GotoXY(0, 2);
	printf("Inquire");
	GotoXY(14, 2);
	printf("I / i key");

	GotoXY(0, 3);
	printf("Reset");
	GotoXY(14, 3);
	printf("R / r key");

	GotoXY(0, 4);
	printf("Quit ");
	GotoXY(14, 4);
	printf("Q / q key");

	GotoXY(0, 5);
	printf("Whole year ");
	GotoXY(14, 5);
	printf("W / w key");

	GotoXY(0, 6);
	printf("-------------------------------");

	GotoXY(0, 8);
	printf("Year");
	GotoXY(9, 8);
	printf("The key to  + :  PageUp");
	GotoXY(9, 9);
	printf("The key to  - :  PageDown");

	GotoXY(0, 11);
	printf("Month");
	GotoXY(9, 11);
	printf("The key to  + :  ↑");
	GotoXY(9, 12);
	printf("The key to  - :     ↓");

	GotoXY(0, 14);
	printf("Day");
	GotoXY(9, 14);
	printf("The key to  + :  →");
	GotoXY(9, 15);
	printf("The key to  - :     ←");
}

void PrintWeek(struct Date *pstTempDate)
{
	if (pstTempDate == NULL){          /* 检查指针是否为空 若为空,退出*/
		printf("This is a null pointer!");
		return;
	}
	int iDay = GetWeekday(pstTempDate->iYear, pstTempDate->iMonth, pstTempDate->iDay);
	printf("%4d-%02d-%02d,", pstTempDate->iYear, pstTempDate->iMonth, pstTempDate->iDay);

	switch (iDay)
	{
	case 0: printf("Sunday!"); break;
	case 1: printf("Monday!"); break;
	case 2: printf("Tuesday!"); break;
	case 3: printf("Wednesday!"); break;
	case 4: printf("Thursday!"); break;
	case 5: printf("Friday!"); break;
	case 6: printf("Saturday!"); break;
	}
}

/*    4.日历显示    */
void PrintCalendar(int iYear, int iMonth, int iDay)
{
	int iOutputDay = 1;    /* 输出的日期 */
	int iError = 0;         /*用以标记日期是否有效*/
	int iDayInLastMon = 0; /* 本月第一个星期在上月的天数 */
	int iWeekday = 0;
	int iRow = 4;

	if (IsLeapYear(iYear)) /*  是闰年就返回1 否则返回0  */
		aiMon[2] = 29;
	else
		aiMon[2] = 28;

	if (iDay > aiMon[iMonth])
	{
		printf("This month(%s) has at most %d days\n", acMon[iMonth], aiMon[iMonth]);
		iError = 1;
	}

	if (iDay <= 0)
	{
		printf("The date should be a positive number\n");
		iError = 1;
	}

	if (iError)           /*如果日期无效,重置为系统当前的日期*/
	{
		printf("Press any key to continue......\n");
		getch();
		iYear = stSystemDate.iYear;
		iMonth = stSystemDate.iMonth;
		iDay = stSystemDate.iDay;
		stCurrentDate = stSystemDate;
		if (IsLeapYear(iYear)) /*  此时由于日期变化了,需要再次修改2月最大天数  */
			aiMon[2] = 29;
		else
			aiMon[2] = 28;
	}

	iNumCurrentMon = aiMon[iMonth];
	iNumLastMon = aiMon[iMonth - 1];

	/*  获取给定该月份1号的星期  */
	iWeekday = iDayInLastMon = GetWeekday(iYear, iMonth, 1);

	system("CLS");
	GotoXY(LAYOUT, 0);
	printf("      The Calendar of %d", iYear);
	GotoXY(LAYOUT + 11, 1);
	printf("%s", acMon[iMonth]);

	GotoXY(LAYOUT, 2);
	PrintUnderline();
	GotoXY(LAYOUT, 3);
	printf(" Sun Mon Tue Wed Thu Fri Sat");
	/* 不输出在本月第一星期中 但不属于本月的日期
	每个日期占用四个空格  */
	GotoXY(LAYOUT, 4);
	PrintSpace(iDayInLastMon * 4);

	while (iOutputDay <= aiMon[iMonth])	/* 所要输出的天数超过所属月最大天数时 退出循环  表示已输出整个月的月历 */
	{
		if (iOutputDay == iDay)
		{
			if (iDay < 10)				/* 只有一位的数与两位数处理不同 */
				printf("  (%d)", iOutputDay);
			else
				printf(" (%2d)", iOutputDay);
		}
		else
			printf("%4d", iOutputDay);
		
		if (iWeekday == 6)							 /*输出为星期六的日期后 换行*/
			GotoXY(LAYOUT, ++iRow);
		iWeekday = iWeekday > 5 ? 0 : iWeekday + 1;  /*如果是星期六 则变为星期日 否则加一即可 再次强调星期日是每个星期的第一天*/
		iOutputDay++;
	}

	GotoXY(LAYOUT, 10);
	PrintUnderline();
	GotoXY(LAYOUT + 2, 11);
	printf("The day you choose is :");
	GotoXY(LAYOUT + 2, 13);
	PrintWeek(&stCurrentDate);
	GotoXY(LAYOUT, 14);
	PrintUnderline();

	GotoXY(LAYOUT + 2, 15);
	printf("Today is:\n");
	GotoXY(LAYOUT + 2, 17);
	PrintWeek(&stSystemDate);
	GotoXY(LAYOUT, 18);
	PrintUnderline();
	PrintInstruction();
	GotoXY(0, 20);
}

void PrintWholeYear(int iYear, int iMonth, int iDay)
{
	int iOutputDay = 1;    /* 输出的日期 */
	int iOutputMonth = 1;  /* 输出的月份*/
	int iError = 0;         /*用以标记日期是否有效*/
	int iDayInLastMon = 0; /* 本月第一个星期在上月的天数 */
	int iWeekday = 0;
	int iRow = 0;
	int iTemp = 3;
	int iCol = 40;
	if (IsLeapYear(iYear))
		aiMon[2] = 29;
	else
		aiMon[2] = 28;
	if (iDay > aiMon[iMonth])
	{
		printf("This month(%s) has at most %d days\n", acMon[iMonth], aiMon[iMonth]);
		iError = 1;
	}

	if (iDay <= 0)
	{
		printf("The date should be a positive number\n");
		iError = 1;
	}

	if (iError)
	{
		printf("Press any key to continue......\n");
		getch();
		iYear = stSystemDate.iYear;
		iMonth = stSystemDate.iMonth;
		iDay = stSystemDate.iDay;
		stCurrentDate = stSystemDate;
	}

	iWeekday = iDayInLastMon = GetWeekday(iYear, 1, 1);
	GotoXY(18, 0);
	printf("The Calendar of the whole %d", iYear);
	if (IsLeapYear(iYear))
		printf("[Leap Year!]\n");
	else
		printf("[Common Year!]\n");

	GotoXY(0, 1);
	printf(" Sun Mon Tue Wed Thu Fri Sat");
	GotoXY(iCol, 1);
	printf(" Sun Mon Tue Wed Thu Fri Sat");

	while (iOutputMonth <= 12)
	{
		iRow = iTemp;
		GotoXY(iCol, iRow - 1);
		PrintUnderline();

		if (iOutputMonth % 2)
			iCol = 0;
		else
		{
			iCol = 40;
			iTemp += 8;
		}
		iOutputDay = 1;

		GotoXY(iCol + 10, iRow);
		printf("%s", acMon[iOutputMonth]);
		GotoXY(iCol, ++iRow);
		PrintSpace(iDayInLastMon * 4);

		if (iOutputMonth == iMonth)
		{
			while (iOutputDay <= aiMon[iOutputMonth])
			{
				if (iOutputDay == iDay)
				{
					if (iDay < 10)
						printf("  (%d)", iOutputDay);
					else
						printf(" (%2d)", iOutputDay);
				}
				else
					printf("%4d", iOutputDay);
				if (iWeekday == 6)
					GotoXY(iCol, ++iRow);
				iWeekday = iWeekday > 5 ? 0 : iWeekday + 1;
				iOutputDay++;
			}
		}
		else
		{
			while (iOutputDay <= aiMon[iOutputMonth])
			{
				printf("%4d", iOutputDay);
				if (iWeekday == 6)
					GotoXY(iCol, ++iRow);
				iWeekday = iWeekday > 5 ? 0 : iWeekday + 1;
				iOutputDay++;
			}
		}

		iOutputMonth++;
		iDayInLastMon = iWeekday;
	}
	iRow = iTemp;
	GotoXY(0, iRow - 1);
	PrintUnderline();
	GotoXY(40, iRow - 1);
	PrintUnderline();
	GotoXY(0, iRow);
	printf("Press any key to return to the main interface!\n");
	getch();
}

/*    5.键盘输入    */
void GetKey()
{
	int iFirst = 1;
	char cKey = '\0', c = '\0';
	while (1)
	{
		PrintCalendar(stCurrentDate.iYear, stCurrentDate.iMonth, stCurrentDate.iDay);
		/*如果是第一次 则打印该语句*/
		if (iFirst){
			GotoXY(0, 19);
			printf("Please read the instruction carefully!\n");
			iFirst = 0;
		}

		cKey = getch();
		if (cKey == -32)
		{
			cKey = getch();
			switch (cKey)
			{

			case UP:
			{
					   if (stCurrentDate.iMonth<12)
						   stCurrentDate.iMonth++;
					   else
					   {
						   stCurrentDate.iYear++;
						   stCurrentDate.iMonth = 1;
					   }
					   break;
			}


			case DOWN:
			{
						 if (stCurrentDate.iMonth > 1)
							 stCurrentDate.iMonth--;
						 else
						 {
							 stCurrentDate.iYear--;
							 stCurrentDate.iMonth = 12;
						 }
						 break;
			}


			case LEFT:
			{
						 if (stCurrentDate.iDay>1)
							 stCurrentDate.iDay--;
						 else
						 {
							 /*若当前日期为1月1日 减一天后则变为上一年的12月31日*/
							 if (stCurrentDate.iMonth == 1)
							 {
								 stCurrentDate.iYear--;
								 stCurrentDate.iMonth = 12;
								 stCurrentDate.iDay = 31;
							 }
							 else
							 {
								 stCurrentDate.iMonth--;
								 stCurrentDate.iDay = 31;
							 }
						 }
						 break;
			}


			case RIGHT:
			{
						  if (stCurrentDate.iDay<iNumCurrentMon)
							  stCurrentDate.iDay++;
						  else
						  {
							  /*若当前日期为12月31日 加一天后则变成下一个年1月1日*/
							  if (stCurrentDate.iMonth == 12)
							  {
								  stCurrentDate.iYear++;
								  stCurrentDate.iMonth = 1;
								  stCurrentDate.iDay = 1;
							  }
							  else
							  {
								  stCurrentDate.iMonth++;
								  stCurrentDate.iDay = 1;
							  }
						  }
						  break;
			}

			case PAGE_UP:
			{
							stCurrentDate.iYear++;
							break;
			}

			case PAGE_DOWN:
			{
							  stCurrentDate.iYear--;
							  break;
			}

			}
		}
		else
		{
			if (cKey == 'I' || cKey == 'i')
			{
				printf("Input date( %d-%02d-%02d ,eg)\n", stSystemDate.iYear, stSystemDate.iMonth, stSystemDate.iDay);
				scanf("%d-%d-%d", &stCurrentDate.iYear, &stCurrentDate.iMonth, &stCurrentDate.iDay);
				CheckDate();
				getchar();
			}

			if (cKey == 'R' || cKey == 'r')
			{
				stCurrentDate = stSystemDate;
			}

			if (cKey == 'Q' || cKey == 'q')
			{
				printf("Do you really want to quit? <Y/N>");
				c = getchar();
				if (c == 'Y' || c == 'y')
					break;
			}

			if (cKey == 'W' || cKey == 'w')
			{
				system("cls");                       /*打印全年日历之前先清屏*/
				PrintWholeYear(stCurrentDate.iYear, stCurrentDate.iMonth, stCurrentDate.iDay);
			}
		}
	}
}

/*    6.主函数    */
int main()
{
	time_t RawTime = 0;
	struct tm * pstTargetTime = NULL;
	time(&RawTime);   //获取当前时间,存rawtime里
	pstTargetTime = localtime(&RawTime);   //获取当地时间

	stSystemDate.iYear = pstTargetTime->tm_year + 1900; /*得到的时间是从1900年1月1日开始的*/
	stSystemDate.iMonth = pstTargetTime->tm_mon + 1;
	stSystemDate.iDay = pstTargetTime->tm_mday;

	stCurrentDate = stSystemDate;
	GetKey();
	return 0;
}
  • 44
    点赞
  • 380
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 8
    评论
评论 8
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

拾亿-唯一

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值