前言:

文章不足之处请麻烦评论指正!

如果你想一步到位直接复制代码自行查看学习,可以在菜单中直接跳转到文章最后的“全部代码”处进行复制。2个文件( .h 和 .c )的代码在“全部代码”中已经整合为一个文件,可直接粘贴进编辑器使用!

一,预备知识

1. 关于 struct tm 结构体

struct tm是C语言中的一个结构体,定义在头文件中。它用于存储时间和日期的信息,我们只需要在程序中包含头文件,就可以使用此结构体中的信息,能够更方便地操作和处理时间。结构体的定义如下:

struct tm {int tm_sec; // 秒,范围从 0 到 59int tm_min; // 分,范围从 0 到 59int tm_hour;// 时,范围从 0 到 23int tm_mday;// 一个月中的日,范围从 1 到 31int tm_mon; // 月份,范围从 0 到 11int tm_year;// 年份,从 1900 开始int tm_wday;// 一周中的日,范围从 0 (周日) 到 6 (周六)int tm_yday;// 一年中的日,范围从 0 到 365int tm_isdst; // 夏令时标识};

2. 计算 星期几 的方式

公式:int w= (d + 2 * m + 3 * (m + 1) / 5 + y + y / 4 – y / 100 + y / 400) % 7;

(其中d表示日,m表示月,y表示年;其中w+1表示星期几)

w 的值将是 0 到 6 之间的一个整数,分别代表一周的不同天,再加 1 准确表示周几。


3. 输出不同颜色的字体

·演示效果:

`其中 \33[0m 为关闭所有属性.

·根据上面的演示,我们可以直接得出结论:在输出时候加上 \033[ 参数…… \033[0m 即可使得在一句打印语句中输出的字体是有颜色的。(使用ANSI转义码控制文本属性)

·格式解析: \033[ 参数1;参数2是样式的开始\33[参数 是样式的结束,如果不结束则代表着后续的输出字符也套用其中的格式。(更多关于c语言字体颜色的内容可自行搜索)

二,代码分段解析

1. 头文件和定义的全局变量

#include"head.h" //包含头文件head.htime_t t;//定义time_t类型的变量tstruct tm* today;//创建结构体指针变量todayint choice = 0;//用于键盘输入选择菜单选项int year, month, day;//全局变量用于记录当前的时间信息int year1, month1, day1; //全局变量用于记录输入要查询的时间

全局变量可以为本文件中其它函数所共用,它的作用域为从定义变量的位置开始到本源文件结束。定义为全局变量可方便函数直接调用,而不需要再进行传参.

2. 头文件head.h

#pragma once#include#include//菜单void menu();//判断给定年份是否是闰年int isLeapYear(int y);//这个函数用于计算返回该月的天数int daysInMonth(int y, int m);//这个函数用于计算给定日期是星期几int dayOfWeek(int y, int m, int d);//可突出显示当前指定年月的月历void displayCalendar(int y, int m, int currentDay);//这个函数用于显示指定年份 year 的全年日历void displayYearCalendar(int y);//这个函数用于验证给定的日期是否有效int isValidDate(int y, int m, int day);//查询指定日期的月历并突出显示void queryDate(int y, int m, int day);//允许用户通过键盘输入来调整日期void adjustDate(int y, int m, int d);//重置时间为当前系统时间void resetTime();

如果使用的是Microsoft Visual Studio进行编写代码,请在代码开头添加:

#define _CRT_SECURE_NO_WARNINGS

2. 主函数main

int main(){int year2, month2, day2;t = time(NULL);//time(&t),获取当前时间戳(秒数)//将时间戳(秒数)转换为 struct tm 结构体,对应本地时区today = localtime(&t);year = today->tm_year + 1900;//年份从1900开始month = today->tm_mon + 1; //月份从1开始day = today->tm_mday;//一个月中的日,0-31do{//获取对应日期为星期几int week = dayOfWeek(year, month, day);//打印菜单,并进行输入选择printf(">>>\033[36;5mNOW: %d-%d-%d · 周%d\033[0m\n", year, month, day, week);menu();printf("\033[1m请输入选择的操作: \033[0m");scanf("%d", &choice);switch (choice){case 1://显示当前的月历,并突出显示今天的日期displayCalendar(year, month, day);break;case 2:system("cls");//清空已有内容displayYearCalendar(year);break;case 3:printf("请输入要查询的日期(YYYY MM DD): ");scanf("%4d %2d %2d", &year1, &month1, &day1);queryDate(year1, month1, day1);break;case 4:printf("请输入修改后的日期(YYYY MM DD): ");scanf("%4d %2d %2d", &year2, &month2, &day2);adjustDate(year2, month2, day2);break;case 0:printf("已退出万年历系统!\n");break;default:printf("输入错误,请重试!\n");break;}} while (choice);return 0;}

主要根据用户使用键盘进行输入选择,然后通过switch语句执行对应的函数。

三,代码中使用到的函数

1,展示选择菜单

void menu(){printf("\033[36m| 1, 获取当前日期 |\033[0m\n");printf("\033[36m| 2, 显示全年日历 |\033[0m\n");printf("\033[36m| 3, 查询日期 |\033[0m\n");printf("\033[36m| 4, 调整日期 |\033[0m\n");printf("\033[36m| 0, 退出 |\033[0m\n");printf("\n");}

2,判断给定年份是否为闰年

int isLeapYear(int y){ if ((y % 4 == 0 && y % 100 != 0) || y % 400 == 0)return 1;//返回1,代表闰年 elsereturn 0;//返回0,代表平年}

计算闰年的方式: :年份可以被4整除但是不能被100整除; :年份能被400整除。


3,计算返回给定月份的天数

int daysInMonth(int y, int m){int days[13] = { 0,31,28,31,30,31,30,31,31,30,31,30,31 };if (m == 2 && isLeapYear(y)){days[2] = 29;}return days[m];}

定义一个长度为13的整型数组存放月份的天数(为了提高代码的可读性,在0索引处置0,则后续的每个索引都可以代表其对应月份的天数),判断给定的年份是否为闰年,是闰年则将2月的天数置为29天,最后返回对应月份的天数。


4,计算给定日期是星期几

int dayOfWeek(int y, int m, int d){if (m < 3){m += 12;y--;}int h = (d + 2 * m + 3 * (m + 1) / 5 + y + y / 4 - y / 100 + y / 400) % 7;//h+1表示某时间是星期几return h + 1;}

“预备知识”部分已介绍了根据给定的日期计算星期的方法!


5,显示当前指定日期的月历

void displayCalendar(int y, int m, int currentDay){printf("\033[33m================== %4d 年 %2d 月 =================\033[0m\n", y,m);printf("日\t一\t二\t三\t四\t五\t六\n");printf("--------------------------------------------------\n");//6*6+2*7int first_day = dayOfWeek(y, m, 1);//获取每月1号是周几int ofday = daysInMonth(y, m);//获取一个月有多少天int count = first_day;for (int i = 0; i <= ofday; i++){//使日期能对齐对应的星期if (i == 0){for (int j = 0; j < count % 7; j++){printf("\t");}}else if (i == currentDay){//突出显示日期(当m等于要查询的时间或等于当前的时间)if (m == month || m == month1){printf("\033[31m%2d\033[0m\t", i);count++;}else{printf("%2d\t", i);count++;}}else{printf("%2d\t", i);count++;}//循环打印一周后换行if (count % 7 == 0){printf("\n");}}printf("\n");}

参数中的currentDay表示需要进行突出显示的日期。


月历打印格式的逻辑:

获取到每个月的一号是星期几和每个月共有多少天,一号是周几则在前面打印对应数量的制表符或空格;

使用一个变量记录一号是周几,每当打印一天的时候这个变量则进行自增++,当到周日(7)时则进行换行后再打印日期,直到一个月中所有天数都打印完成。

在打印所有的天数中可进行判断当前要打印的天数是否等于currentDay,如果等于则进行突出显示,不等于则进行普通打印。


6,显示指定年份的全年日历

void displayYearCalendar(int y){int i = 0;for (i = 1; i <= 12; i++){displayCalendar(y, i, day);printf("\n");}}

在循环中调用打印月历的函数即可打印出对应日期的年历


7,验证给定的日期是否有效

int isValidDate(int y, int m, int d){if (y < 1900 || (m 12) || (d  daysInMonth(y, m)))return 0;//返回0,代表格式错误elsereturn 1;//返回1,代表格式正确}


8,查询指定日期的月历并突出显示

void queryDate(int y, int m, int d){int choice = 0;int flag = isValidDate(y, m, d);if (flag){printf("请选择: \033[36m| 1.月历 - 2.年历 |\033[0m\n");scanf("%d", &choice);if (choice == 1){displayCalendar(y, m, d);}else if(choice == 2){//将关于日期的全局变量修改为指定的日期,在突出显示后再修改回系统值.day = d;month = m;displayYearCalendar(y);day = today->tm_mday;month = today->tm_mon + 1;}elseprintf("请重新选择!\n");}elseprintf("无效的日期!\n");}


9,重置时间为当前系统时间

void resetTime(){year = today->tm_year+1900;month = today->tm_mon+1;day = today->tm_mday;printf("\033[31m已重置为系统时间!\033[0m\n");}

当输入无效的日期时,将时间信息重置为初始时间(此程序为当前系统时间)。


10,将当前日期调整为新输入的日期,无效则重置时间

void adjustDate(int y, int m, int d){int flag = isValidDate(y, m, d);if (flag){year = y;month = m;day = d;printf("当前的时间已修改为:%d年%d月%d日\n", y, m, d);}else{printf("无效的日期!\n");//输入日期无效则进行重置时间resetTime();}}


四,全部代码

希望文章对你有所帮助。有问题和建议可评论区一起讨论!


以下代码可直接粘贴进编辑器运行。

#define _CRT_SECURE_NO_WARNINGS#include#include#include//菜单void menu();//判断给定年份是否是闰年int isLeapYear(int y);//这个函数用于计算返回该月的天数int daysInMonth(int y, int m);//这个函数用于计算给定日期是星期几int dayOfWeek(int y, int m, int d);//可突出显示当前指定年月的月历void displayCalendar(int y, int m, int currentDay);//这个函数用于显示指定年份 year 的全年日历void displayYearCalendar(int y);//这个函数用于验证给定的日期是否有效int isValidDate(int y, int m, int day);//查询指定日期的月历并突出显示void queryDate(int y, int m, int day);//允许用户通过键盘输入来调整日期void adjustDate(int y, int m, int d);//重置时间为当前系统时间void resetTime();//定义全局变量time_t t;struct tm* today;int choice = 0;int year, month, day;int year1, month1, day1;int main(){int year2, month2, day2;t = time(NULL);//time(&t),获取当前时间戳(秒数)//将时间戳(秒数)转换为 struct tm 结构体,对应本地时区today = localtime(&t);year = today->tm_year + 1900;//年份从1900开始month = today->tm_mon + 1;//月份从1开始day = today->tm_mday;do{int week = dayOfWeek(year, month, day);printf(">>>\033[36;5mNOW: %d-%d-%d · 周%d\033[0m\n", year, month, day, week);menu();printf("\033[1m请输入选择的操作: \033[0m");scanf("%d", &choice);switch (choice){case 1://显示当前的月历,并突出显示今天的日期displayCalendar(year, month, day);break;case 2:system("cls");//清空已有内容displayYearCalendar(year);break;case 3:printf("请输入要查询的日期(YYYY MM DD): ");scanf("%4d %2d %2d", &year1, &month1, &day1);queryDate(year1, month1, day1);break;case 4:printf("请输入修改后的日期(YYYY MM DD): ");scanf("%4d %2d %2d", &year2, &month2, &day2);adjustDate(year2, month2, day2);break;case 0:printf("已退出万年历系统!\n");break;default:printf("输入错误,请重试!\n");break;}} while (choice);return 0;}//菜单void menu(){printf("\033[36m| 1, 获取当前日期 |\033[0m\n");printf("\033[36m| 2, 显示全年日历 |\033[0m\n");printf("\033[36m| 3, 查询日期 |\033[0m\n");printf("\033[36m| 4, 调整日期 |\033[0m\n");printf("\033[36m| 0, 退出 |\033[0m\n");printf("\n");}//判断给定年份 year 是否是闰年int isLeapYear(int y){if ((y % 4 == 0 && y % 100 != 0) || y % 400 == 0)return 1;//闰年elsereturn 0;//平年}//用于计算返回该月的天数int daysInMonth(int y, int m){int days[13] = { 0,31,28,31,30,31,30,31,31,30,31,30,31 };if (m == 2 && isLeapYear(y)){days[2] = 29;}return days[m];}//计算给定日期的星期几int dayOfWeek(int y, int m, int d){if (m < 3){m += 12;y--;}int h = (d + 2 * m + 3 * (m + 1) / 5 + y + y / 4 - y / 100 + y / 400) % 7;/*余数的值将是0(星期日)到6(星期六)之间的一个整数,分别代表一周的不同天,再加1准确表示周几)*///h+1表示某时间是星期几return h + 1;}//显示当前指定年月的月历void displayCalendar(int y, int m, int currentDay){printf("\033[33m================== %4d 年 %2d 月 =================\033[0m\n", y,m);printf("日\t一\t二\t三\t四\t五\t六\n");printf("--------------------------------------------------\n");//6*6+2*7int first_day = dayOfWeek(y, m, 1);//获取每月1号的周数int ofday = daysInMonth(y, m);//获取一个月有多少天int count = first_day;for (int i = 0; i <= ofday; i++){//使日期能对齐对应的星期if (i == 0){for (int j = 0; j < count % 7; j++){printf("\t");}}else if (i == currentDay){//突出显示日期if (m == month || m == month1){printf("\033[31m%2d\033[0m\t", i);count++;}else{printf("%2d\t", i);count++;}}else{printf("%2d\t", i);count++;}//循环打印一周后换行if (count % 7 == 0){printf("\n");}}printf("\n");}//这个函数用于显示指定年份的全年日历void displayYearCalendar(int y){int i = 0;for (i = 1; i <= 12; i++){displayCalendar(y, i, day);printf("\n");}}//验证给定的日期是否有效int isValidDate(int y, int m, int d){if (y < 1900 || (m 12) || (d  daysInMonth(y, m)))return 0;//格式错误elsereturn 1;//格式正确}//查询指定日期的月历并突出显示void queryDate(int y, int m, int d){int choice = 0;int flag = isValidDate(y, m, d);if (flag){printf("请选择: \033[36m| 1.月历 - 2.年历 |\033[0m\n");scanf("%d", &choice);if (choice == 1){displayCalendar(y, m, d);}else if(choice == 2){//将关于日期的全局变量修改为指定的日期,在突出显示后再修改回系统值.day = d;month = m;displayYearCalendar(y);day = today->tm_mday;month = today->tm_mon + 1;}elseprintf("请重新选择!\n");}elseprintf("无效的日期!\n");}//将当前日期调整为新输入的日期,无效则重置时间void adjustDate(int y, int m, int d){int flag = isValidDate(y, m, d);if (flag){year = y;month = m;day = d;printf("当前的时间已修改为:%d年%d月%d日\n", y, m, d);}else{printf("无效的日期!\n");//输入日期无效则进行重置时间resetTime();}}//重置时间为当前系统时间void resetTime(){year = today->tm_year+1900;month = today->tm_mon+1;day = today->tm_mday;printf("\033[31m已重置为系统时间!\033[0m\n");getchar();Sleep(1300);system("cls");}