项目需求分析

项目需求

1.日历显示:显示一年日历,包括阳历、阴历、节气等信息。

2.日历查询:查询某年、某月日历。

3.设置备忘录:记录某天的行程、事项等备忘信息,以备查询。

4.行程提醒:可设置未来某一天的任务安排

项目背景

  1. 万年历是人们日常生活不可或缺的部分。

  2. 电子万年历可以使人们生活更加便利

  3. 万年历的备忘录功能提高了人们的生活效率

  4. 万年历的需求容易理解

  5. 万年历模块化程度很高,并且模块之间的联系清晰明了。

  6. C语言适合开发字符界面程序文件读写

项目介绍

开发一套软件系统,实现万年历最常用的功能,以日历为核心,围绕日历实现其查询行程事项、备忘录以及查看某年、某月节日和节气等功能。该电子平台还应具有一定界面友好性,给客户良好的体验,并且具有拓展性,可方便拓展其它功能。

角色

角色名称 功能操作
用户 查看日历、查询某年、某月的日历、设置备忘录、设置行程提醒

程序结构图

结构

业务模块汇总

功能编号 功能简述 功能描述
0 查看日历 查看当月日历
1 查看一年日历 显示当年日历,包括阳历、阴历、节气
2 查询某月、某日的日历 查看某年、某月日历
3 设置备忘录 设置某天备忘录,包括但不限于行程、事项
4 设置行程提醒 设置未来某天的行程提醒

项目开发分析

农历数据提取与分析

农历数据来源

通过网上查阅资料可知:

万年历是中国古代传说中最古老的一部太阳历。万年历是记录一定时间范围内(比如100年或更多)的具体阳历与阴历的日期的年历,方便有需要的人查询使用。万年只是一种象征,表示时间跨度大。

阴历是按月亮的盈亏变化来制定的。

阴阳合历,是结合太阳和月亮运行的周期制定的。一年按太阳的运行分为二十四节气,又按照月亮的运行分为月。小月、大月十二个加起来,只有三百五十四天或三百五十五天。两者相比,相差约十一天。为了协调二者,古人采取“设闰”的办法来处置,若干年中就有一年是十三个月的。

很多人都一直在找换阴阳历的公式,高平子所著《学历散论》解读了古历的变更和阴阳历的缺陷,才知道由月球转动的不稳定不规则,确定无公式可寻。这也是古代中国每百年必改历的原因。

😥由此发现,万年历并没有固定的计算方法,需要根据日月运行轨迹观察计算得到数据。

😝大多数万年历作者描述农历数据是从日历上一年一年查阅出来,然后再整理、压缩才得到的,容易出错。

👍因此,我们在网上找到了一个农历提取工具,获取农历及其相关数据。

😀最后,通过算法实现公历和农历的相互转换。

农历数据提取工具:(待更新)

农历数据分析

农历数据由提取工具生成为头文件供使用。

农历数据包括:月份信息(3个字节)、24节气(6个字节)、数九、入梅、出梅及三伏信息(2个字节)

农历月份信息

//农历月份信息。一年用3个字节表示,采用十六进制(即24位二进制)。
//+———————————————————————————————————–+
//| 第23位 | 第22-17位 | 第16-13位 | 第12-0位 |
//|———–+————————————+—————-+——————————– ——|
//| 保留 | 农历正月初一的年内序数 | 闰月 | 一个比特对应一个月份大小|
//+———————————————————————————————————–+
//月份大小数据是月份小的在低位,月份大的在高位,即正月在最低位。
//以1900年为例,3个字节的数据展开成二进制位:
// 0 011110 1000 1 0 1 1 0 1 1 0 1 0 0 1 0

解释说明:

0:保留位 暂时无用

01110:转换十进制为30 (序数从0开始,即正月初一是这年的第31天)

1000:转换十进制为8,即闰8月。

1 0 1 1 0 1 1 0 1 0 0 1 0:对应十二个月和闰月的天数,0即29天,1即30天。 顺序:农历12月 11月… 闰8月 8月 …1月

月份

24节气信息

//二十四节气信息。一年用6个字节表示,每个节气使用两比特数据。
//+———————————————————————————-+
//| 第一字节最高两位 | 第一字节其余6位至第六字节共46个位 |
//|—————————+——————————————————|
//|小寒的年内序数减3| 每个节气距离上一节气的天数,共23组 |
//+———————————————————————————–+
//小寒的年内序数已给出,剩下的23个节气分别对应这23组数据,有以下含义:
//+——————————————————————————–+
//| 二进制位 | 意义 | 描述 |
//|—————+——–+——————————————————|
//| 00 | 14天 | 当前对应的节气距离上一节气为14天 |
//|—————+——–+—————————————————- -|
//| 01 | 15天 | 当前对应的节气距离上一节气为15天 |
//|—————+——–+—————————————————- -|
//| 10 | 16天 | 当前对应的节气距离上一节气为16天 |
//|—————+———+—————————————————–|
//| 11 | 17天 | 当前对应的节气距离上一节气为17天 |
//+———————————————————————————+
//由上表可以看出,除小寒以外的其余23个节气的两比特数据加上14就是距离上一节气的天数。
//节气顺序:
//小寒 大寒 立春 雨水 惊蛰 春分 清明 谷雨 立夏 小满 芒种 夏至
//小暑 大暑 立秋 处暑 白露 秋分 寒露 霜降 立冬 小雪 大雪 冬至

下面为节气数据,配合后面的节气索引表使用:

下面为节气数据的索引表,数据对应“wSTSource[72]”中的索引。
比如,起始年份(1600年)的节气数据为:wSTSource[bySTIndex[0]],wSTSource[bySTIndex[1]],wSTSource[bySTIndex[2]]这三个16位(6字节)数据。

节气索引

数九、入梅、出梅及三伏信息

1599年冬至日在1600年的年内序数,这个数据将被用在1600年“数九”的计算上。
定义了 char const cPreDongzhiOrder=-10;

//每年数九、入梅、出梅及三伏信息,一年用两个字节表示。
//+—————————————————+
//| 第15-11位 | 第10-6位 | 第5-1位 | 最末位 |
//|————+————+————+————|
//| 入梅 | 出梅 | 初伏 | 末伏 |
//+—————————————————+
//1.“一九”即是冬至,往后到“九九”的每个“九”相差9天,可顺利推算出来,故“数九”信息省略。
//2.“三伏”天的“中伏”在“初伏”后10天,而“末伏”在“中伏”后10天或20天,因此“中伏”信息省略。
//入梅信息:该天数加上150为入梅这一日的年内序数。
//出梅信息:该天数加上180为出梅这一日的年内序数。
//初伏信息:该天数加上180为初伏这一日的年内序数。
//末伏信息:若该位为“1”,表示末伏距离初伏30天,为“0”表示末伏距离初伏20天。

数九

程序设计

所使用的头文件

1
2
3
4
5
6
7
#include <stdio.h>
#include <Windows.h>
#include <stdlib.h>
#include <string.h>
#include <memory.h>
#include <math.h>
#include <time.h>

宏定义

1
2
3
4
5
6
7
8
9
10
11
typedef int BOOL;

#define CURU 72 // 方向键上
#define CURD 80 // 方向键下
#define CURL 75 // 方向键左
#define CURR 77 // 方向键右
#define ESC 27 // ESC
#define ENTER 13 // ENTER

#define FALSE 0
#define TRUE 1

定义的全局变量

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
int Today_year;    // 本机时间 年
int Today_month; // 本机时间 月
int Today_day; // 本机时间 日
char Today_week[3];// 本机时间 星期

char* Riming[30] = { "初一","初二","初三","初四","初五","初六","初七","初八","初九","初十","十一","十二","十三","十四","十五","十六","十七","十八","十九","二十","廿一","廿二","廿三","廿四","廿五","廿六","廿七","廿八","廿九","三十" }; // 农历日名
char* Yueming[12] = { "正月","二月","三月","四月","五月","六月","七月","八月","九月","十月","冬月","腊月" }; // 农历月名
char* Tiangan[10] = { "甲", "乙", "丙", "丁", "戊", "己", "庚", "辛", "壬", "癸" }; // 天干
char* Dizhi[12] = { "子", "丑", "寅", "卯", "辰", "巳", "午", "未", "申", "酉", "戌", "亥" }; // 地支
char* Shengxiao[12] = { "鼠", "牛", "虎", "兔", "龙", "蛇", "马", "羊", "猴", "鸡", "狗", "猪" }; // 生肖
char* Jieqi[24] = { "立春", "雨水", "惊蛰", "春分", "清明", "谷雨", "立夏", "小满", "芒种", "夏至", "小暑", "大暑", "立秋", "处暑", "白露", "秋分", "寒露", "霜降", "立冬", "小雪", "大雪", "冬至", "小寒", "大寒" }; // 节气
char* Xingqi[7] = { "星期日","星期一","星期二","星期三","星期四","星期五","星期六" }; // 星期
char* Shujiu[9] = { "一九","二九","三九","四九","五九","六九","七九","八九","九九" }; // 数九
char* Meiyu[2] = { "入梅","出梅" }; // 入梅和出梅
char* Sanfu[3] = { "初伏","中伏","末伏" }; // 三伏

int DayOrdinal[13]={0,31,59,90,120,151,181,212,243,273,304,334,365}; // 平年年内序数(即第几天 - 1)
int DayOrdinalLeap[13]={0,31,60,91,121,152,182,213,244,274,305,335,366}; // 闰年年内序数

函数声明

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
//判断闰年,参数:年份,返回值:0-平年,1-闰年
BOOL IsLeapYear(int sYear);

//计算日期在年内的序数,参数:年,月,日,年内序数,返回值:0-失败,1-成功
BOOL GetDayOrdinal(int sYear, int wMonth, int wDay, int* nDays);

//从年内序数计算月、日,参数:年,年内序数,月,日,返回值:0-失败,1-成功
BOOL GetDateFromOrdinal(int sYear, int sOrdinal, int* wMonth, int* wDay);

//获取农历新年的公历年内序数,参数:农历年,返回值:农历新年的公历年内序数
int LunarGetNewYearOrdinal(int sLunarYear);

//获取农历月的天数,参数:农历年,农历月,是否为闰月,返回值:该农历月的天数,为0代表参数无效
int LunarGetDaysofMonth(int sLunarYear, int wLunarMonth, BOOL bLeapMonth);

//展开大小月数据表(某一年的),参数:农历年,从上一年十一月开始到当前年份(闰)十二月的每月天数,返回值:0-失败,1-成功
BOOL LunarExpandDX(int sLunarYear, int iDayOfMonth[15]);

//获取农历某一年的闰月情况,参数:农历年,返回值,该年的闰月月份,0表示无闰月
int LunarGetLeapMonth(int sLunarYear);

//获取农历月份信息
BOOL GetMonthInfo(int wYear, int* puData);

// 公历转农历
BOOL Gongli2Nongli(int sYear, int wMonth, int wDay, int* sLunarYear, int* wLunarMonth, int* wLunarDay, BOOL* bLeapMonth);

// 农历转公历
BOOL Nongli2Gongli(int sLunarYear, int wLunarMonth, int wLunarDay, BOOL bLeapMonth, int* sYear, int* wMonth, int* wDay);

//得到指定年份的节气信息,首个是小寒
BOOL GetJieQi(int sYear, int wMonth, int wJieQi[2]);

//计算星期,返回-1表示输入的年月日不正确或者超出年份范围
int GetDayOfWeek(int sYear, int wMonth, int wDay);

//计算某个月的天数,返回天数,如果返回0表示年或月有误
int GetDaysOfMonth(int sYear, int wMonth);

//查询某一年的数九及三伏,参数:公历年、上一年冬至(即一九)、初伏、末伏
BOOL GetExtremeSeason(int sYear, int* sYijiu, int* wChuFu, int* wMoFu);

//查询某一年的入梅、出梅,参数:公历年,入梅,出梅
BOOL GetMeiYu(int sYear, int* wRuMeiOrd, int* wChuMeiOrd);

//公历节日及节气显示,参数:公历年、公历月、公历日
int G_HolidayShow(int sYear, int wMonth, int wDay);

//农历节日及杂项显示,参数:农历年、农历月、农历日、农历闰月
BOOL L_HolidayShow(int sLYear, int iLMonth, int iLDay, int iLeapMonth);

//显示日历
void ShowCalendar(int sYear, int wMonth, int today);

//节气查询
void JQ();

//获取本地时间
void initCurrentTime();

//显示欢迎界面
void ShowWelcome();

//显示菜单界面
void ShowMenu();

//显示查询界面
void ShowQuery();

//设置字体颜色
void SetColor(int ForeColor, int BackGroundColor);

函数定义详解

main函数

1
2
3
4
5
6
7
8
9
10
11
12
int main(void)
{
SetConsoleTitleA("电子万年历"); // 设置控制台窗口标题
system("color 81"); // 设置控制台背景颜色
while (1)
{
initCurrentTime(); // 获取本地时间
ShowWelcome(); // 显示主菜单
}
return 0;
}
//使用while循环,便于进入下级菜单和返回主菜单。

IsLeapYear

函数声明:BOOL IsLeapYear(int sYear)

输入:int sYear(公元年份)

输出:若年份为平年,返回FALSE;若年份为闰年,返回TRUE。

说明:判断年份是否为闰年

1
2
3
4
5
6
7
8
9
10
BOOL IsLeapYear(int sYear)
{
if (sYear % 4 == 0 && sYear % 100 != 0 || sYear % 400 == 0)//判断闰年的条件
{
return TRUE;
}else
{
return FALSE;
}
}

GetDayOrdinal

函数声明:BOOL GetDayOrdinal(int sYear, int wMonth, int wDay,int *nDays)

输入:int sYear(公元年)、 int wMonth(月)、int wDay(日)、int *nDays(距元旦的天数)

输出:成功返回1,失败返回-1。

说明:从日期算出距离元旦的序数(天数-1),结果保存到nDays中。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
BOOL GetDayOrdinal(int sYear, int wMonth, int wDay,int *nDays)
{

int ret=0;
ret=IsLeapYear(sYear);//判断是否为闰年

if (ret==1)
{
*nDays=DayOrdinalLeap[wMonth-1]+wDay-1;//元旦为序数0,因此减1
}
else
{
*nDays=DayOrdinal[wMonth-1]+wDay-1;
}
return 1;
}

GetDateFromOrdinal

函数声明:BOOL GetDateFromOrdinal(int sYear,int sOrdinal,int* wMonth,int* wDay)

输入:int sYear(公元年)、int sOrdinal(年内序数)、int* wMonth(月)、int* wDay(日)

输出:成功返回TRUE,失败返回FALSE。

说明:通过传入的年内序数计算日期,将结果保存到wMonth和wDay中。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
BOOL GetDateFromOrdinal(int sYear,int sOrdinal,int* wMonth,int* wDay)
{

int ret=0,i=0;

ret=IsLeapYear(sYear);
if (ret!=-1)
{
//年符合,则计算;
*wMonth=0;
for (i=0;i<12;i++)
{
if (ret==1)
{
if (sOrdinal>=DayOrdinalLeap[i]&&sOrdinal<DayOrdinalLeap[i+1])//找出月份
{
*wMonth=i+1;
*wDay=sOrdinal-DayOrdinalLeap[i]+1;//计算出“日”
break;
}
}else
{
if (sOrdinal>=DayOrdinal[i]&&sOrdinal<DayOrdinal[i+1])
{
*wMonth=i+1;
*wDay=sOrdinal-DayOrdinal[i]+1;
break;
}
}
}
}
return TRUE;
}

LunarGetNewYearOrdinal

函数声明:int LunarGetNewYearOrdinal(int sLunarYear)

输入:int sLunarYear(农历新年)

输出:成功返回农历新年对应公历的年内序数,失败返回0。

说明:获取农历新年的公历年内序数

1
2
3
4
5
6
7
8
9
int LunarGetNewYearOrdinal(int sLunarYear)
{
int uData=0;
if (GetMonthInfo(sLunarYear,&uData)==FALSE)
{
return 0;
}
return (int)((uData>>17)&0x3F);
}

LunarGetDaysofMonth

函数声明:int LunarGetDaysofMonth(int sLunarYear,int wLunarMonth,BOOL bLeapMonth)

输入:int sLunarYear(农历年)、int wLunarMonth(农历月)、BOOL bLeapMonth(是否闰月)

输出:成功返回天数,失败返回0。

说明:获取农历月的天数。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
int LunarGetDaysofMonth(int sLunarYear,int wLunarMonth,BOOL bLeapMonth)
{
int i=0;
int DX_data=0;//该年大小月情况
int uData=0;
int Acc_LeapMonth=0;

if (GetMonthInfo(sLunarYear,&uData)==FALSE)
{
return 0;
}
DX_data=(int)(uData&0x1FFF);//整年大小月情况
Acc_LeapMonth=LunarGetLeapMonth(sLunarYear);//获取闰月月份
if (bLeapMonth)//指定查询的当前月是闰月
{
for (i=0;i<wLunarMonth;i++)
{
DX_data>>=1;//右移一位,即从末尾开始找该闰月月份所在的位
}
}else
{
if (Acc_LeapMonth>0)//存在闰月
{
if (wLunarMonth<=Acc_LeapMonth)//月份在闰月之前,倒找需要多找一位
{
for (i=0;i<wLunarMonth-1;i++)
{
DX_data>>=1;
}
}else
{
for (i=0;i<wLunarMonth;i++)//月份在闰月之后
{
DX_data>>=1;
}
}
}else
{
for (i=0;i<wLunarMonth-1;i++)//无闰月
{
DX_data>>=1;
}
}
}
if (DX_data&0x1)
{
return 30;//大月
}else
{
return 29;//小月
}
}

LunarExpandDX

函数声明:BOOL LunarExpandDX(int sLunarYear,int iDayOfMonth[15])

输入:int sLunarYear(农历年)、int iDayOfMonth[15](保存上一年11月到今年12月+闰月 共15个月)

输出:成功返回TRUE 失败返回FALSE 结果保存在 iDayOfMonth 中。

说明:展开大小月数据表。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
BOOL LunarExpandDX(int sLunarYear,int iDayOfMonth[15])
{
int i=0,pos=0,iLeapMonth=0;//循环变量,数组写入位置,闰月
if (sLunarYear<START_YEAR||sLunarYear>=END_YEAR)
{
return FALSE;
}
for (i=0;i<15;i++)
{
iDayOfMonth[i]=0;
}
iLeapMonth=LunarGetLeapMonth(sLunarYear-1);//取得前一农历年的闰月情况
if (iLeapMonth<11)//一月至十月的闰月
{
iDayOfMonth[pos]=LunarGetDaysofMonth(sLunarYear-1,11,0);//取上一年十一月天数
pos++;
iDayOfMonth[pos]=LunarGetDaysofMonth(sLunarYear-1,12,0);//取上一年十二月天数
pos++;
}else
{
iDayOfMonth[pos]=LunarGetDaysofMonth(sLunarYear-1,11,0);//取上一年十一月的天数
pos++;
if (iLeapMonth==11)//闰十一月
{
iDayOfMonth[pos]=LunarGetDaysofMonth(sLunarYear-1,11,1);//取上一年闰十一月的天数
pos++;
iDayOfMonth[pos]=LunarGetDaysofMonth(sLunarYear-1,12,0);//取上一年十二月天数
pos++;
}else if (iLeapMonth==12)
{
iDayOfMonth[pos]=LunarGetDaysofMonth(sLunarYear-1,12,0);//取上一年十二月天数
pos++;
iDayOfMonth[pos]=LunarGetDaysofMonth(sLunarYear-1,12,1);//取上一年闰十二月天数
pos++;
}
}
iLeapMonth=LunarGetLeapMonth(sLunarYear);//获取当前农历年的闰月情况
if (iLeapMonth==0)//无闰月
{
for (i=0;i<12;i++)
{
iDayOfMonth[pos]=LunarGetDaysofMonth(sLunarYear,i+1,0);//取每个农历月天数
pos++;
}
}else
{
for (i=0;i<12;i++)
{
if (i==iLeapMonth)
{
iDayOfMonth[pos]=LunarGetDaysofMonth(sLunarYear,i,1);//取闰月的天数
pos++;
}
iDayOfMonth[pos]=LunarGetDaysofMonth(sLunarYear,i+1,0);//取非闰月的天数
pos++;
}
}
return TRUE;
}

LunarGetLeapMonth

函数声明:int LunarGetLeapMonth(int sLunarYear)

输入:int sLunarYear(农历年)

输出:成功返回闰月情况,失败返回0。

说明:获取农历某一年的闰月情况。

1
2
3
4
5
6
7
8
9
10
11
int LunarGetLeapMonth(int sLunarYear)
{
int data=0;
int wLeapMonth=0;
if (GetMonthInfo(sLunarYear,&data)==FALSE)
{
return 0;
}
wLeapMonth=(int)((data>>13)&0x0F);
return wLeapMonth;
}

BOOL GetMonthInfo

函数声明:BOOL GetMonthInfo(int wYear,int* puData)

输入:int wYear(公历年)、int* puData(保存月份信息)

输出:成功返回TRUE,失败返回FALSE。

说明:获取月份信息。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
BOOL GetMonthInfo(int wYear,int* puData)
{
int iStartPos=(wYear-START_YEAR)*3;
int uData=0;
if (wYear<START_YEAR||wYear>=END_YEAR)
{
return FALSE;
}
uData=byMonthInfo[iStartPos];
uData<<=8;
uData|=byMonthInfo[iStartPos+1];
uData<<=8;
uData|=byMonthInfo[iStartPos+2];
if (puData)
{
*puData=uData;
}
return TRUE;
}

Gongli2Nongli

函数声明:

输入:

输出:

说明:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
BOOL Gongli2Nongli(int sYear,int wMonth,int wDay,int* sLunarYear,int* wLunarMonth,int* wLunarDay,BOOL* bLeapMonth)
{
int DaysNum=0,LunarNewYear=0,i=0,remain_days=0;//年内序数,农历新年的公历年内序数,循环变量,剩余天数
int DaysOfLunarMonth[15]={0};//存放农历月份天数
int iLeapMonthPre=0,iLeapMonth=0;//农历上一年闰月,今年闰月
int ret=GetDayOrdinal(sYear,wMonth,wDay,&DaysNum);
if (ret==0)
{
return 0;//日期不正确
}
*sLunarYear=sYear;
LunarNewYear=LunarGetNewYearOrdinal(*sLunarYear);
LunarExpandDX(*sLunarYear,DaysOfLunarMonth);//获取月份天数,数组从上一年十一月开始到今年(闰)十二月,包含闰月
iLeapMonthPre=LunarGetLeapMonth(*sLunarYear-1);
if (iLeapMonthPre==0)
{
iLeapMonth=LunarGetLeapMonth(*sLunarYear);//上一年没有闰月,则查询今年闰月
}
*bLeapMonth=FALSE;
remain_days=DaysNum-LunarNewYear;//距离农历新年天数
if (iLeapMonthPre>10)
{
i=3;//今年正月在“DaysOfLunarMonth”中的索引,上一年十一月或十二月有闰月
}else
{
i=2;//上一年十一月和十二月无闰月时,今年正月的索引
}
if (LunarNewYear>DaysNum)//早于农历新年
{
*sLunarYear-=1;//农历年减1
while(remain_days<0)
{
i--;//第一次先减去是因为当前i是正月,减1表示上一年十二月(或闰十二月)
remain_days+=DaysOfLunarMonth[i];//加上上一年十二月、十一月的总天数(含闰月)直到日数大于0
}
if (iLeapMonthPre>10)//如果上一年十一月或十二月存在闰月
{
if (iLeapMonthPre==11)//闰十一月
{
if (i==0)//十一月(即在闰月之前)
{
*wLunarMonth=11+i;//转换到月份
}else
{
*wLunarMonth=10+i;
if (*wLunarMonth==iLeapMonthPre)
{
*bLeapMonth=TRUE;
}
}
}else if (iLeapMonthPre==12)//闰十二月
{
if (i<2)//在闰月之前
{
*wLunarMonth=11+i;
}else
{
*wLunarMonth=10+i;
if (*wLunarMonth==iLeapMonthPre)
{
*bLeapMonth=TRUE;
}
}
}
}else
{
*wLunarMonth=11+i;
}
*wLunarDay=remain_days;
}else
{
while (remain_days>=DaysOfLunarMonth[i])
{
remain_days-=DaysOfLunarMonth[i];//寻找农历月
i++;//移至下个月
}
if (iLeapMonthPre>10)
{
*wLunarMonth=i-2;
}else
{
if (iLeapMonth>0)
{
if (i-2<iLeapMonth)
{
*wLunarMonth=i-1;
}else
{
*wLunarMonth=i-2;
if (*wLunarMonth==iLeapMonth)
{
*bLeapMonth=TRUE;
}
}
}else
{
*wLunarMonth=i-1;
}
}
*wLunarDay=remain_days;
}
*wLunarDay+=1;//索引转换到数量
return TRUE;
}

Nongli2Gongli

函数声明:

输入:

输出:

说明:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
BOOL Nongli2Gongli(int sLunarYear,int wLunarMonth,int wLunarDay,BOOL bLeapMonth,int* sYear,int* wMonth,int* wDay)
{
int DaysOfLunarMonth[15]={0};//存放农历月份天数
int iLeapMonthPre=0,iLeapMonth=0;//农历年闰月
int LunarNewYear=0,i=0,remain_days=0;//年内序数,农历新年的公历年内序数,循环变量,剩余天数
int iDaysofYear=0;
if (sLunarYear<START_YEAR||sLunarYear>=END_YEAR||wLunarMonth<1||wLunarMonth>12||wLunarDay<1||wLunarDay>30)
{
return FALSE;//年、月、日检查
}
if (bLeapMonth)
{
if (LunarGetLeapMonth(sLunarYear)!=wLunarMonth)
{
return FALSE;//闰月检查
}
}
if (wLunarDay>LunarGetDaysofMonth(sLunarYear,wLunarMonth,bLeapMonth))
{
return FALSE;//大小月检查
}
LunarExpandDX(sLunarYear,DaysOfLunarMonth);//大小月表(农历每月天数)
LunarNewYear=LunarGetNewYearOrdinal(sLunarYear);//找到正月初一的公历年内序数
iLeapMonth=LunarGetLeapMonth(sLunarYear);//找出农历年的闰月
remain_days+=LunarNewYear;//加上正月初一的序数
if (iLeapMonthPre>10)
{
i=3;//今年正月在“DaysOfLunarMonth”中的索引,上一年十一月或十二月有闰月
}else
{
i=2;//上一年十一月和十二月无闰月时,今年正月的索引
}
if (iLeapMonth==0)
{
if (iLeapMonthPre>10)
{
for (;i<wLunarMonth+2;i++)
{
remain_days+=DaysOfLunarMonth[i];//年内序数累加
}
}else
{
for (;i<wLunarMonth+1;i++)
{
remain_days+=DaysOfLunarMonth[i];//年内序数累加
}
}
}else
{
if (wLunarMonth<iLeapMonth||(bLeapMonth==FALSE&&wLunarMonth==iLeapMonth))//在闰月之前
{
for (;i<wLunarMonth+1;i++)
{
remain_days+=DaysOfLunarMonth[i];
}
}else
{
for (;i<wLunarMonth+2;i++)
{
remain_days+=DaysOfLunarMonth[i];
}
}
}
remain_days+=wLunarDay-1;//减1是因为日名转到序数
GetDayOrdinal(sLunarYear,12,31,&iDaysofYear);//获取公历年总天数
iDaysofYear++;//从序数转到天数
*sYear=sLunarYear;
if ((remain_days+1)>iDaysofYear)
{
remain_days-=iDaysofYear;
*sYear+=1;//下一年
}
GetDateFromOrdinal(*sYear,remain_days,wMonth,wDay);
return TRUE;
}

GetJieQi

函数声明:

输入:

输出:

说明:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
BOOL GetJieQi(int sYear,int wMonth,int wJieQi[2])
{
int index=0;//对应每公历年首个节气所在字节的索引
int wXiaoHanOrder=0;//小寒年内序数
int wJQData=0;//节气数据
int wCurJQData=0;//当前计算的节气数据
int wDays=0;//当前节气距离该年小寒的天数
int i=0;
int iRem=0;
if (sYear<START_YEAR||sYear>=END_YEAR||wMonth>12||wMonth<1)
{
return FALSE;
}
index=(sYear-START_YEAR)*3;//对应每公历年首个节气所在字节的索引
wJQData=wSTSource[bySTIndex[index]];
wXiaoHanOrder=(int)((wJQData>>14)+3);//加上3,转为小寒的年内序数
wCurJQData=(wJQData>>12)&0x03;//当前计算的节气与上一节气的天数差信息
if (wMonth==1)
{
wJieQi[0]=wXiaoHanOrder+1;//加1转到日期
wJieQi[1]=wCurJQData+14+wXiaoHanOrder+1;//大寒:小寒的年内序数加上距离小寒的天数
return TRUE;
}
wDays=wCurJQData+14;//距离小寒的天数,当前为大寒距离小寒的天数
wDays+=wXiaoHanOrder;//加上小寒,转为年内序数
for (i=1;i<wMonth;i++)
{
iRem=i%4;
wCurJQData=(wJQData>>(18-((iRem+1)<<2)))&0x03;
wDays+=wCurJQData+14;
wCurJQData=(wJQData>>(16-((iRem+1)<<2)))&0x03;
wDays+=wCurJQData+14;
if (iRem==3)
{
wJQData=wSTSource[bySTIndex[index+(i+1)/4]];
}
}
GetDateFromOrdinal(sYear,wDays,&wMonth,&wJieQi[1]);//wMonth中的第二个节气
GetDateFromOrdinal(sYear,wDays-wCurJQData-14,&wMonth,&wJieQi[0]);//第一个节气
return TRUE;
}

GetDayOfWeek

函数声明:

输入:

输出:

说明:

1
2
3
4
5
6
7
8
9
10
11
12
13
int GetDayOfWeek(int sYear,int wMonth,int wDay)
{
int DayofWeek=0;
int uDayOrd=0;
if (GetDayOrdinal(sYear,wMonth,wDay,&uDayOrd)==0)
{
return 0;
}
uDayOrd++;//一年中的第几天,因为GetDayOrdinal所得到的是索引,因此要加一
sYear--;
DayofWeek=(sYear+sYear/4-sYear/100+sYear/400+uDayOrd)%7;//这个只是算星期的通用公式
return DayofWeek;
}

GetDaysOfMonth

函数声明:

输入:

输出:

说明:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
int GetDaysOfMonth(int sYear,int wMonth)
{
int days1=0,days2=0;
int ret=0;
if (wMonth==12)
{
return 31;//这里为了简便,判断12月就直接返回
}
ret=GetDayOrdinal(sYear,wMonth,1,&days1);//本月1日在年内的序数
if (ret==0)
{
return ret;
}
wMonth++;
ret=GetDayOrdinal(sYear,wMonth,1,&days2);//下个月1日的年内序数
if (ret==0)
{
return ret;
}
ret=days2-days1;//下个月1日的序数减本月1日的序数
return ret;
}

GetExtremeSeason

函数声明:

输入:

输出:

说明:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
BOOL GetExtremeSeason(int sYear,int* sYijiu,int* wChuFu,int* wMoFu)
{
int wjq[2]={0};
int ET_index=sYear-START_YEAR;//数九、梅雨及三伏的年份索引
if (sYear<START_YEAR||sYear>=END_YEAR)
{
return FALSE;
}
if (sYear==START_YEAR)
{
*sYijiu=cPreDongzhiOrder;
}else
{
GetJieQi(sYear-1,12,wjq);
*sYijiu=wjq[1]-32;
}
*wChuFu=((wExtermSeason[ET_index]&0x3E)>>1)+180;
*wMoFu=(*wChuFu)+((wExtermSeason[ET_index]&0x01)==1?30:20);
return TRUE;
}

GetMeiYu

函数声明:

输入:

输出:

说明:

1
2
3
4
5
6
7
8
9
10
11
BOOL GetMeiYu(int sYear, int* wRuMeiOrd, int* wChuMeiOrd)
{
int ET_index = sYear - START_YEAR;//数九、梅雨及三伏的年份索引
if (sYear < START_YEAR || sYear >= END_YEAR)
{
return FALSE;
}
*wRuMeiOrd = ((wExtermSeason[ET_index] & 0xF800) >> 11) + 150;
*wChuMeiOrd = ((wExtermSeason[ET_index] & 0x07C0) >> 6) + 180;
return TRUE;
}

G_HolidayShow

函数声明:

输入:

输出:

说明:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
//公历节日及节气显示,参数:公历年、公历月、公历日
int G_HolidayShow(int sYear,int wMonth,int wDay)
{
int wJQ_date[2]={0};//节气编号
int iDayOrdial=0;//日期的年内序数
int ET_index=sYear-START_YEAR;//数九、梅雨及三伏的年份索引
int iDayofweek_1st=0;//月首星期
int iDongzhi=0;//冬至
int wrumei=0,wchumei=0,wchufu=0,wmofu=0;
int sshujiu=0;
int i=0;
if (GetJieQi(sYear,wMonth,wJQ_date)==1)
{
if (wJQ_date[0]==wDay)
{
printf(" %s\t",Jieqi[((wMonth<<1)+20)%24]);//该月第一个节气
return 1;
}else if (wJQ_date[1]==wDay)
{
printf(" %s\t",Jieqi[((wMonth<<1)+21)%24]);//该月第二个节气
return 1;
}
}
GetDayOrdinal(sYear,12,wJQ_date[1],&iDongzhi);//转换到年内序数
if (GetDayOrdinal(sYear,wMonth,wDay,&iDayOrdial))
{
/*if (iDayOrdial==wExtermSeason[ET_index+5])
{
printf("[%s]\t",Shujiu[0]);//一九(即冬至,不需要判断了,上面冬至已经优先输出了)
return 1;
}else */
GetExtremeSeason(sYear,&sshujiu,&wchufu,&wmofu);
GetMeiYu(sYear,&wrumei,&wchumei);
if (iDayOrdial==iDongzhi+9)
{
printf(" %s\t",Shujiu[1]);//二九
return 2;
}
if (iDayOrdial>=(sshujiu+9)&&iDayOrdial<sshujiu+81)
{
for (i=0;i<8;i++)
{
if (iDayOrdial==sshujiu+(i+1)*9)
{
printf("(%s)\t",Shujiu[i+1]);//三九至九九
return 1;
}
}
}else if (iDayOrdial==wrumei)
{
printf("(%s)\t",Meiyu[0]);//入梅
return 1;
}else if (iDayOrdial==wchumei)
{
printf("(%s)\t",Meiyu[1]);//出梅
return 1;
}else if (iDayOrdial==wchufu)
{
printf("(%s)\t",Sanfu[0]);//初伏
return 1;
}else if (iDayOrdial==wchufu+10)
{
printf("(%s)\t",Sanfu[1]);//中伏
return 1;
}else if (iDayOrdial==wmofu)
{ printf("(%s)\t",Sanfu[2]);//末伏
return 1;
}
}
if (wMonth==1)
{
if (wDay==1)
{
printf(" 元旦\t");
return 1;
}
}else if (wMonth==2)
{
if (wDay==14)
{
printf("情人节\t");
return 1;
}
}else if (wMonth==3)
{
switch (wDay)
{
case 5:
if (sYear>=1963)
{
printf("学雷锋日");
return 1;
}
break;
case 8:
if (sYear>=1900)
{
printf("妇女节\t");
return 1;
}
break;
case 12:
if (sYear>=1928)
{
printf("植树节\t");
return 1;
}
break;
case 15:
if (sYear>=1983)
{
printf("消权日\t");
return 1;
}
break;
default:
break;
}
}else if (wMonth==4)
{
if (wDay==1)
{
printf("愚人节\t");
return 1;
}
}else if (wMonth==5)
{
if (sYear>=1872)
{
iDayofweek_1st=GetDayOfWeek(sYear,wMonth,1);//取月首星期
if ((wDay+iDayofweek_1st-1)%7==0&&((wDay+iDayofweek_1st-1+(iDayofweek_1st==0?7:0))/7)==2)//5月第二个周日母亲节
{
printf("母亲节\t");
return 1;
}
}
switch (wDay)
{
case 1:
if (sYear>=1890)
{
printf("劳动节\t");
return 1;
}
break;
case 4:
if (sYear>=1919)
{
printf("青年节\t");
return 1;
}
break;
case 12:
if (sYear>=1912)
{
printf("护士节\t");
return 1;
}
break;
case 31:
if (sYear>=1989)
{
printf("无烟日\t");
return 1;
}
break;
default:
break;
}
}else if (wMonth==6)
{
if (sYear>=1910)
{
iDayofweek_1st=GetDayOfWeek(sYear,wMonth,1);//取月首星期
if ((wDay+iDayofweek_1st-1)%7==0&&((wDay+iDayofweek_1st-1+(iDayofweek_1st==0?7:0))/7)==3)//6月第三个周日父亲节
{
printf("父亲节\t");
return 1;
}
}
switch (wDay)
{
case 1:
if (sYear>1949)
{
printf("儿童节\t");
return 1;
}
break;
case 5:
if (sYear>=1972)
{
printf("环境日\t");
return 1;
}
break;
case 26:
if (sYear>=1987)
{
printf("禁毒日\t");
return 1;
}
break;
default:
break;
}
}else if (wMonth==7)
{
switch (wDay)
{
case 1:
if (sYear>=1921)
{
printf("中共诞辰");
return 1;
}
break;
case 7:
if (sYear>=1937)
{
printf("抗战纪念");
return 1;
}
break;
default:
break;
}
}else if (wMonth==8)
{
if (wDay==1)
{
if (sYear>=1933)
{
printf("建军节\t");
return 1;
}
}
}else if (wMonth==9)
{
switch (wDay)
{
case 3:
if (sYear>=1945)
{
printf("抗战胜利");
return 1;
}
break;
case 10:
if (sYear>=1985)
{
printf("教师节\t");
return 1;
}
break;
default:
break;
}
}else if (wMonth==10)
{
if (wDay==1)
{
if (sYear>=1949)
{
printf(" 国庆\t");
return 1;
}
}
}else if (wMonth==11)
{
if (sYear>=1941)
{
iDayofweek_1st=GetDayOfWeek(sYear,wMonth,1);//取月首星期
if ((wDay+iDayofweek_1st-1)%7==4&&((wDay+iDayofweek_1st-1+(iDayofweek_1st==4?7:0))/7)==4)//11月第四个周四感恩节
{
printf("感恩节\t");
return 1;
}
}
if (wDay==1)
{
printf("万圣节\t");
return 1;
}
}else if (wMonth==12)
{
switch (wDay)
{
case 10:
if (sYear>=1948)
{
printf("人权日\t");
return 1;
}
break;
case 12:
if (sYear>=1936)
{
printf("西安事变");
return 1;
}
break;
case 24:
printf("平安夜\t");
return 1;
case 25:
printf("圣诞节\t");
return 1;
default:
break;
}
}
return 0;
}

L_HolidayShow

函数声明:

输入:

输出:

说明:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
//农历节日及杂项显示,参数:农历年、农历月、农历日、农历闰月
BOOL L_HolidayShow(int sLYear,int iLMonth,int iLDay,int iLeapMonth)
{
int LeapMonth=LunarGetLeapMonth(sLYear);//取该年农历闰月
int DayofLM_12=0;//农历十二月的总天数
if (iLeapMonth==0)
{
if (iLMonth==1)
{
switch (iLDay)
{
case 1:
printf(" 春节\t");
return TRUE;
case 15:
printf("元宵节\t");
return TRUE;
default:
break;
}
}else if (iLMonth==2)
{
if (iLDay==2)
{
printf("春龙节\t");
return TRUE;
}
}else if (iLMonth==5)
{
if (iLDay==5)
{
printf("端午节\t");
return TRUE;
}
}else if (iLMonth==7)
{
if (iLDay==7)
{
printf(" 七夕\t");
return TRUE;
}else if (iLDay==15)
{
printf("中元节\t");
return TRUE;
}
}else if (iLMonth==8)
{
if (iLDay==15)
{
printf("中秋节\t");
return TRUE;
}
}else if (iLMonth==9)
{
if (iLDay==9)
{
printf("重阳节\t");
return TRUE;
}
}else if (iLMonth==10)
{
if (iLDay==1)
{
printf("祭祖节\t");
return TRUE;
}else if (iLDay==15)
{
printf("下元节\t");
return TRUE;
}
}else if (iLMonth==12)
{
if (LeapMonth!=12)
{
DayofLM_12=LunarGetDaysofMonth(sLYear,12,0);//非闰十二月
if (iLDay==8)
{
printf("腊八节\t");
return TRUE;
}else if (iLDay==23)
{
printf(" 小年\t");
return TRUE;
}else if (iLDay==DayofLM_12)//农历十二月的最后一天是除夕
{
printf(" 除夕\t");
return TRUE;
}
}
}
}else
{
if (iLeapMonth==LeapMonth)
{
if (iLeapMonth==12)
{
DayofLM_12=LunarGetDaysofMonth(sLYear,12,1);//闰十二月
if (iLDay==8)
{
printf("腊八节\t");
return TRUE;
}else if (iLDay==23)
{
printf(" 小年\t");
return TRUE;
}else if (iLDay==DayofLM_12)//农历十二月的最后一天是除夕
{
printf(" 除夕\t");
return TRUE;
}
}
}
}
return FALSE;//没有节日等
}

initCurrentTime

函数声明:

输入:

输出:

说明:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
//获取本地时间
void initCurrentTime()
{
// 获取当前时间
time_t timep;
struct tm* p;
time(&timep);
p = gmtime(&timep);

static char* week[] = { "日","一","二","三","四","五","六" };
strcpy(Today_week, week[p->tm_wday]);

Today_year = 1900 + p->tm_year;
Today_month = 1 + p->tm_mon;
Today_day = p->tm_mday;
}

ShowWelcome

函数声明:

输入:

输出:

说明:

1
2
3
4
5
6
7
8
9
10
11
void ShowWelcome()
{
system("mode con cols=100 lines=20"); // 设置控制台窗口大小
printf("\n\n\n\n\t\t\t\t ☆☆☆万年历查询系统☆☆☆\n\n");
printf("\t\t ════════════════════════════════════════════\n\n");
printf("\t\t\t\t%04d年 %02d月 %02d日 星期%s\n\n", Today_year, Today_month, Today_day, Today_week);
printf("\t\t\t\t 今日大事提醒:无\n\n");
printf("\t\t\t\t →按任意键进入系统←\n\n");
getch();
ShowMenu();
}

ShowMenu

函数声明:

输入:

输出:

说明:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
void ShowMenu()
{
while (1)
{
system("mode con cols=100 lines=20"); // 设置控制台窗口大小
system("cls");

printf("\n\n\n\n\t\t\t\t 欢迎使用电子万年历\n\n");
printf("\t\t\t\t ╔═══════════════════╗\n");
printf("\t\t\t\t ╬ 1.显示日历 ╬\n");
printf("\t\t\t\t ╬═══════════════════╬\n");
printf("\t\t\t\t ╬ 2.日历查询 ╬\n");
printf("\t\t\t\t ╚═══════════════════╝\n\n");
printf("\t\t\t\t 请输入功能序号:");

fflush(stdin);
int choice = 0;
scanf("%d", &choice);
switch (choice)
{
case 1:
ShowCalendar(Today_year, Today_month, Today_day);
break;
case 2:
ShowQuery();
default:
break;
}
}
}

ShowCalendar

函数声明:

输入:

输出:

说明:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
void ShowCalendar(int sYear,int wMonth, int today)
{
//要显示一个月的月历,有以下几个要点:
//1.该月1日的星期
//2.该月的总天数
//3.该月1日对应的农历以及农历月大小,有时甚至需要知道下个月甚至下下个月的大小
system("mode con cols=60 lines=30"); // 设置控制台窗口大小
while (1)
{
system("cls");
int iDayofweek_1st = 0;//该月1日星期
int sNYear = 0;
int wNMonth = 0, wNDay = 0;//农历年、月、日
BOOL bNLeapMonth = FALSE;//闰月标志
int iDaysofmonth = 0;//公历月总天数
int iNDaysofmonth = 0;//农历月总天数
int iPos = 0, iGDayIdx = 1, iNDayIdx = 0, iNindex = 1;//输出位置、公历日,农历日,农历输出天数(同步iDaysofmonth)
int iFillFlag = 0;//填充标志,0为公历星期填充,1为农历星期填充,2为公历填充,3为农历填充
iDayofweek_1st = GetDayOfWeek(sYear, wMonth, 1);//取得1日的星期

iDaysofmonth = GetDaysOfMonth(sYear, wMonth);//得到本月总天数
Gongli2Nongli(sYear, wMonth, 1, &sNYear, &wNMonth, &wNDay, &bNLeapMonth);//得到公历1日的农历
iNDaysofmonth = LunarGetDaysofMonth(sNYear, wNMonth, bNLeapMonth);//得到农历月总天数
iNDayIdx = wNDay;//取出农历日

int todayIpos = -1; // 标记今天,方便给农历标记颜色
int todayIpos2 = -1; // 标记参数today的位置,方便移动
int flag_break = 0;

printf("\n\t\t%d年%d月 农历 ", sYear, wMonth);
if (bNLeapMonth == 1)
{
printf("闰");
}
if (iNDaysofmonth == 29)
{
printf("%s(小)\n\n", Yueming[wNMonth - 1]);
}
else if (iNDaysofmonth == 30)
{
printf("%s(大)\n\n", Yueming[wNMonth - 1]);
}
for (iPos = 0; iPos < 7; iPos++)
{
printf("%s\t", Xingqi[iPos]);
}
printf("\n_______________________________________________________\n\n");
iPos = 0;
while (iNindex <= iDaysofmonth)
{
if (iFillFlag == 0 || iFillFlag == 2)
{
//颜色标记今天
if (today == iGDayIdx)
{
todayIpos = iPos;
todayIpos2 = iPos;
SetColor(4, 8);
}

if (iFillFlag == 0)
{
while (iPos < iDayofweek_1st)//直到星期填充完
{
printf("\t");
iPos++;//位置增加
}
}
if (G_HolidayShow(sYear, wMonth, iGDayIdx) == 0)
{
printf(" %02d\t", iGDayIdx);//输出公历
}
iPos++;
iGDayIdx++;
if (iGDayIdx > iDaysofmonth)
{
printf("\n");
iPos = 0;//位置重新回到开始
iFillFlag = 3;
}
else
{
if (iPos == 7)//输出要换行了
{
printf("\n");
iPos = 0;//位置重新回到开始
if (iFillFlag == 0)
{
iFillFlag = 1;//切换到农历星期填充
}
else
{
iFillFlag = 3;
}
}
}
}
else if (iFillFlag == 1 || iFillFlag == 3)
{
if (iPos == todayIpos)
{
todayIpos = -1;
SetColor(4, 8);
}

if (iFillFlag == 1)
{
while (iPos < iDayofweek_1st)//直到星期填充完
{
printf("\t");
iPos++;//位置增加
}
}

if (iNDayIdx <= iNDaysofmonth)
{
if (L_HolidayShow(sNYear, wNMonth, iNDayIdx, bNLeapMonth) == 0)
{
if (iNDayIdx == 1)
{
printf(" %s", Yueming[wNMonth - 1]);//公历月首为农历月首
if (iNDaysofmonth == 30)
{
printf("大\t");
}
else
{
printf("小\t");
}
}
else
{
printf(" %s\t", Riming[iNDayIdx - 1]);//没有节日、节气等杂项输出时,输出农历日名
}
}
}
else
{
Gongli2Nongli(sYear, wMonth, iNindex, &sNYear, &wNMonth, &wNDay, &bNLeapMonth);//算出农历下一个月
iNDaysofmonth = LunarGetDaysofMonth(sNYear, wNMonth, bNLeapMonth);//重新得到农历月总天数
iNDayIdx = wNDay;
if (L_HolidayShow(sNYear, wNMonth, iNDayIdx, bNLeapMonth) == 0)
{
//没有节日、节气等杂项输出时,输出农历月名
if (bNLeapMonth == 1)
{
printf("闰");
}
printf("%s", Yueming[wNMonth - 1]);
if (iNDaysofmonth == 29)
{
printf("小");
}
else if (iNDaysofmonth == 30)
{
printf("大");
}
if (bNLeapMonth == 0)
{
printf("\t");
}
}
}
iPos++;
iNDayIdx++;//农历日增加
iNindex++;//农历已填充天数增加
if (iPos == 7)
{
if (!(iGDayIdx >= iDaysofmonth && (iDaysofmonth + iDayofweek_1st) % 7 == 0))
{
printf("\n\n");
}
iPos = 0;
iFillFlag = 2;
}
}
SetColor(1, 8);
}
printf("\n_______________________________________________________\n\n");

//监视热键
fflush(stdin);
while (1)
{
int key = getch();
if (key == CURU)
{
today -= 7;
if (today <= 0)
{
if (wMonth - 1 < 1) // 应该是上一年
{
sYear--;
wMonth = 12;
today = 31 + today;
}
else
{
wMonth--;
today = GetDaysOfMonth(sYear, wMonth) + today;
}
}
break;
}
else if (key == CURD)
{
today += 7;
if (today > GetDaysOfMonth(sYear, wMonth))
{
if (wMonth + 1 > 12) // 应该是下一年
{
today = today - GetDaysOfMonth(sYear, wMonth);
sYear++;
wMonth = 1;
}
else
{
today = today - GetDaysOfMonth(sYear, wMonth);
wMonth++;
}
}
break;
}
else if (key == CURL)
{
today -= 1;
if (today <= 0)
{
if (wMonth - 1 < 1) // 应该是上一年
{
sYear--;
wMonth = 12;
today = 31 + today;
}
else
{
wMonth--;
today = GetDaysOfMonth(sYear, wMonth) + today;
}
}
break;
}
else if (key == CURR)
{
today += 1;
if (today > GetDaysOfMonth(sYear, wMonth))
{
if (wMonth + 1 > 12) // 应该是下一年
{
today = today - GetDaysOfMonth(sYear, wMonth);
sYear++;
wMonth = 1;
}
else
{
today = today - GetDaysOfMonth(sYear, wMonth);
wMonth++;
}
}
break;
}
else if (key == ESC)
{
flag_break = 1;
break;
}
}

if (flag_break == 1)
{
break;
}
}
}

ShowQuery

函数声明:

输入:

输出:

说明:

1
2
3
4
5
6
7
8
9
10
11
12
void ShowQuery()
{
while (1)
{
system("mode con cols=60 lines=30"); // 设置控制台窗口大小
system("cls");

printf("\n\n\n\t\t\t\t欢迎使用万年历日历查询功能");
fflush(stdin);
getchar();
}
}

SetColor

函数声明:

输入:

输出:

说明:

1
2
3
4
5
6
//设置字体颜色 	1 蓝色 4 红色 8 灰色
void SetColor(int ForeColor, int BackGroundColor)
{
HANDLE handle = GetStdHandle(STD_OUTPUT_HANDLE);//获取当前窗口句柄
SetConsoleTextAttribute(handle, ForeColor + BackGroundColor * 0x10);//设置颜色
}

运行效果

欢迎界面:

wnl1

主界面:

WNL2

显示日历:

WNL3

总结

👍初步实现万年历的功能,可以实现公历和农历的转换

🐷实现日历显示(1600年—2099年),并可以方向键控制光标并翻页。

😋核心功能已实现,待实现功能,可自行添加:

1.日历查询:查询某一年、某个月的日历(调用函数打印)

2.备忘录:设置并查看某天的备忘录(文件读写)

3.提醒事项:设置某天的提醒闹钟(文件读写)