C语言绘制日历

教育   2024-10-11 01:06   宁夏  

前言

编写程序的关键不在于代码量的多少,而在于是否融入了独特的思考与创新。缺乏这些,技术的成长之路将会停滞不前。有时,我们会遇到一些被戏称为“屎山代码”的实例,这些代码中充斥着层层嵌套的if语句,甚至在if语句内部还嵌套着循环,导致代码几乎无法阅读,更谈不上任何创新或优化。

如何编写一个日历思路

关于如何编写一个日历的问题,我经过长时间的思考后,今天决定给出一个解答方案。在构建日历的过程中,一个核心问题是确定任意一天是星期几。幸运的是,前人已经为我们提供了有效的解决方案——蔡勒公式。这一公式专门用于计算给定日期的星期数,网络上有着丰富的相关资料和详细的推导过程,因此在这里我不再重复介绍。

int dayOftheWeekThisYear(int year){  int i = ((5 * (year - 1) / 4 - (year - 1) / 100 + (year - 1) / 400) % 7) + 1;  return i;  //蔡勒公式,此处是计算这一年的第一天是星期几}

若已知某一年一月一日是星期几,我们就可以推算出该年中任何月份一号所对应的星期数。虽然最直观的方法是逐日累加来计算,但这显然效率不高。更高效的做法是计算两个日期之间的天数差,随后将这个差值加上起始日期的星期数,再对7取余,即可迅速得出目标月份一号是星期几。值得庆幸的是,对于日期差的计算,Easyx官网已有解决方案。

// 计算日期差// 编译环境:VS2017,C++ 语言//#include <stdio.h>
// 计算从 0001-1-1 起的天数int countdays(int y, int m, int d){ if (m < 3) y--, m += 12; return 365 * y + (y >> 2) - y / 100 + y / 400 + (153 * m - 457) / 5 + d - 306;}
int main(){ // 输入目标日期 int year, month, day; scanf_s("%d-%d-%d", &year, &month, &day);
// 输出当前日期与 1949-10-1 相差的天数 printf("%d\n", countdays(year, month, day) - countdays(1949, 10, 1));
return 0;}

这个代码涉及到的公式推导,官方上有详细的过程,如果感兴趣可以去了解一下。

根据以上的思路,我们就可计算出来某月一号是星期几了。知道该月份一号是星期几之后,我们就可以依此顺着往下排列日期,将这个月的日历编写出来。但是如何计算某年某月有多少天,这时候我们就举起拳头,开始数一月大二月小三月大......

对于某年某月有多少天这个问题,解决方法很多种,有人用15行代码可以搞定,也有人可以用一行代码搞定,但是无论如何,它的原理都是一样的。我前面文章也专门出过一期关于某年某月有多少天?

使用一行代码解决

m<1||m>12 ? 0 : m^2&&m^4&&m^6&&m^9&&m^11 ? 31 : m^2 ? 28+!(y%400||y%4&&!y%100) : 30

有了以上的理论基础,使用Easyx只是将它表现出来就行。这里我们直接获取本地时间,绘制出这个月的日历。

源码

///////////////////////////////////////////////////// 程序名称:Easyx日历// 编译环境:Mictosoft Visual Studio 2022, EasyX_20200315(beta)// 作  者:luoyh <2864292458@qq.com>// 公 众 号:C语言研究// 最后修改:2024-10-10//
#include<graphics.h>#include<conio.h>
SYSTEMTIME t; //定义变量保存当前时间 void Drawtime(); // 实时更新时间void DrawWeek(); // 绘制星期void DrawDays(); // 绘制日期int dayOftheWeekThisYear(int year);int dayOftheWeekThisYearQueryMonth(int year, int month);int countdays(int y, int m, int d); // 计算从 0001-1-1 起的天数int monthdays(int y, int m); // 计算某月的天数int main(){ initgraph(530, 420); setbkcolor(RGB(220, 216, 207)); cleardevice(); BeginBatchDraw(); while (true) { Drawtime(); DrawWeek(); DrawDays(); FlushBatchDraw(); } EndBatchDraw(); _getch(); return 0;}
void Drawtime() // 实时更新时间{ setfillcolor(RGB(72, 255, 205)); solidrectangle(10, 10, 515, 407); setfillcolor(RGB(70, 141, 255)); solidrectangle(10, 10, 515, 32); setbkmode(TRANSPARENT); settextcolor(RGB(1, 0, 241)); settextstyle(20, 0, L"黑体"); RECT r = { 10, 10, 515, 32 }; SYSTEMTIME t; //定义变量保存当前时间 wchar_t s2[126]; GetLocalTime(&t); swprintf_s(s2, 126, L"%d年%d月%d日 %d:%02d:%02d", t.wYear, t.wMonth, t.wDay, t.wHour, t.wMinute, t.wSecond); drawtext(s2, &r, DT_CENTER | DT_VCENTER | DT_SINGLELINE);}void DrawWeek() // 绘制星期{ settextstyle(15, 0, L"宋体"); settextcolor(BLACK); TCHAR str[25]; setlinecolor(WHITE); setfillcolor(YELLOW); for (int i = 0; i < 7; i++) { str[0] = L"星"[0]; str[1] = L"期"[0]; str[2] = L"日一二三四五六"[i]; str[3] = L""[0]; fillrectangle(10 + 72 * i, 32, 82 + 72 * i, 56); RECT r = { 10 + 72 * i,32,82 + 72 * i,56 }; drawtext(str, &r, DT_CENTER | DT_VCENTER | DT_SINGLELINE); }
}void DrawDays() // 绘制日期{ GetLocalTime(&t); int weeks = dayOftheWeekThisYearQueryMonth(t.wYear, t.wMonth); // 获取该月1号是星期几 int WEEKSJ = weeks; int days = monthdays(t.wYear, t.wMonth); // 获取该月天数 TCHAR str_day[25]; settextcolor(RGB(0, 21, 218)); // 设置输出效果为抗锯齿 (VC6 / VC2008 / VC2010 / VC2012) LOGFONT f; gettextstyle(&f); // 获取当前字体设置 f.lfHeight = 40; // 设置字体高度为 40 wcscpy_s(f.lfFaceName, _T("微软雅黑")); // 设置字体为“黑体”(高版本 VC 推荐使用 _tcscpy_s 函数) f.lfQuality = ANTIALIASED_QUALITY; // 设置输出效果为抗锯齿 f.lfWeight = 1000; settextstyle(&f); // 设置字体样式 int GD = 0; int KD = 0; for (int i = 0; i < days; i++) { _stprintf_s(str_day, _T("%d"), i + 1);
if ((WEEKSJ + i) % 7 == 0) { GD++; KD = 0; weeks = 0; settextcolor(RED); } if ((WEEKSJ + i) % 7 == 6) { settextcolor(RED); } RECT r = { 10 + 72 * (KD + weeks),60 + 60 * GD,82 + 72 * (KD + weeks),110 + 60 * GD }; drawtext(str_day, &r, DT_CENTER | DT_VCENTER | DT_SINGLELINE);
if ((i + 1) == t.wDay) // 如果是今天的日期 { setfillcolor(BLUE); fillrectangle(10 + 72 * (KD + weeks), 60 + 60 * GD, 82 + 72 * (KD + weeks), 110 + 60 * GD); settextcolor(WHITE); drawtext(str_day, &r, DT_CENTER | DT_VCENTER | DT_SINGLELINE); } settextcolor(RGB(0, 21, 218)); KD++; }}
int dayOftheWeekThisYear(int year){ int i = ((5 * (year - 1) / 4 - (year - 1) / 100 + (year - 1) / 400) % 7) + 1; return i; //蔡勒公式,此处是计算这一年的第一天是星期几}
//查询某年某月的第一天星期几,需要调用上一个函数实现int dayOftheWeekThisYearQueryMonth(int year, int month){ int totalDays = countdays(year, month, 1) - countdays(year, 1, 1); totalDays = totalDays + dayOftheWeekThisYear(year); return (totalDays % 7); //把这个天数和加上之前计算的查询年的第一天的星期几在进行计算,即可求出查询月的第一天是星期几}
int countdays(int y, int m, int d){ if (m < 3) y--, m += 12; return 365 * y + (y >> 2) - y / 100 + y / 400 + (153 * m - 457) / 5 + d - 306;}
int monthdays(int y, int m){ if (m == 2) return ((y % 4 == 0 && y % 100 != 0) || y % 400 == 0) ? 29 : 28; else return 31 - (m - 3) % 5 % 2;}

改进和优化

该程序在现有基础上仍有两大优化方向。首先,为了使其成为一个功能完备的日历应用,我们需要补充农历数据的算法。这包括但不限于农历月份、天数、节气等复杂计算,从而确保用户能够查询到准确的农历信息。

其次,为了提升用户体验,我们需要增强程序的交互性。具体而言,使用户能够通过鼠标轻松调节年份和月份,实时查看并切换日历页面。这样的设计不仅提高了程序的易用性,还使得查看不同日期的日历信息变得更加直观和便捷。

C语言研究
写给自己的笔记,时常写写,时常看看,仅此而已。