一种农历、节气数据极限压缩算法



很多人都写过万年历程序,也一直都在抱怨农历与节气的不规律,导致在处理这些数据时头大。我也试过很多方法,但对数据的压缩率以及计算效率都不满意。经过了很长时间的努力,终于想到了一种方法,它比目前网络上类似方法都要好,现在就奉献给大家。

直入主题,数据压缩的核心思想,就是依靠“年内序数”这个概念。何为“年内序数”?就是以该年的公历1月1日为起始值0,往后每过一天就加1,往前每过一天就减1,要体现“年内”,基本上范围只能是这一年内的任意一天,往前或者往后超出这一年的,姑且也算作“年内”吧,这在某些特定场合可能会用到,比如在计算“数九”信息时。

  1. 农历数据:

第23位

第22至17位

第16至13位

第12至0位

保留

农历正月初一的年内序数

闰月

月份大小信息

因此,农历数据使用24个位,即3字节。

上述四个块,分别有如下含义:

    ①保留位始终为0;

    ② 农历正月初一距离公历元旦的天数;

    ③ 0表示无闰月,1至12表示闰月月份;

    ④ 从低位到高位分别对应从正月到(闰)十二月的每个月的大小,“1”表示大月,即该月有30天,“0”表示小月,即该月有29天。

以1900年为例:

数据:0x3D,0x16,0xD2,

组合起来为:0x3D16D2,

展开为二进制:01111010001011011010010。

二进制位

0

11110

1000

1011011010010

含义

保留

正月初一年内序数30

转为日期为1900年1月31日

闰八月

十二月

十一月小

十月

九月

闰八月小

八月

七月

六月小

五月

四月小

三月小

二月

正月小

有了上述数据,可以轻松得推算出农历某一天的公历日历和公历某一天的农历日期,转换非常方便。

    2. 节气数据

    经统计,公历年的首个节气均为“小寒”。

第47至46位

第45至0位

小寒的年内序数减3

从大寒到冬至每个节气2个位,表示距上一节气天数

其中第一部分,在使用时,两位数据需要加上3,才能得到小寒的年内序数,第二部分,第45至44位,表示了大寒距离小寒的天数,第43至42位,表示立春距离大寒的天数,……以此类推。

知道了小寒的年内序数,也知道每个节气距离上一节气的天数,就可以很顺利地推算出每个节气的日期了。

对第二部分数据中的两位二进制数有以下含义。

二进制位

含义

描述

00

表示14天

距离上一节气的天数为14天

01

表示15天

距离上一节气的天数为15天

10

表示16天

距离上一节气的天数为16天

11

表示17天

距离上一节气的天数为17天

从上表可以看出,二进制是连续的,表示的天数也是连续的,因此在计算过程当中可以简化,比如计算最后一个节气“冬至”,只要把前面的所有组加起来,即把从第47至0位每两个位为一组,累加起来,再加上3,再加上14×23,就得到了冬至的年内序数。

节气的顺序是:小寒、大寒、立春、雨水、惊蛰、春分、清明、谷雨、立夏、小满、芒种、夏至、小暑、大暑、立秋、处暑、白露、秋分、寒露、霜降、立冬、小雪、大雪、冬至。

 

而通过大量数据比对与分析,发现很多年份的部分数据是相同的,因此,又对上述数据进行了进一步的压缩,将原先的一个16位数据压缩成了8位,并附带了一个小体积的恢复数组。年份跨度越大,压缩比越高。

 

    3. 数九、伏日、梅雨信息

第15至11位

第10至6位

第5至1位

第0位

入梅信息

出梅信息

初伏信息

末伏信息

    省略的信息:①从“一九”到“九九”,因为冬至即为“一九”,且每个“九”相差9天,可以很方便推算;②“中伏”因为是在初伏后10天,因此也省略。

入梅信息转换为入梅的年内序数需要加上150。

出梅信息转换为出梅的年内序数需要加上180。

初伏信息转换为初伏的年内序数需要加上180。

末伏信息若为“0”表示距离初伏20天,若为“1”表示距离初伏30天。

 

讲述了以上压缩方法,那么,源数据从哪里来?压缩要靠手工?

来源当然是从日历上来了,手工压缩容易出错,很有可能吃力不讨好,在文章的最后,将给出一个提取工具,全自动的。

提取到的文件如下(限于篇幅,仅举例从2000年到2019年):

 

#define START_YEAR 2000	//定义数据起始年份(公历)
#define END_YEAR 2020	//定义数据终止年份(不包含该年)

//1999年农历十月及以后的闰月索引,对应cPreMonth中的序号,当前为-1,表示1999年农历十月以后无闰月。
char const cPreLeapIndex=-1;

//1999年农历十月及以后的月份,每月初一在2000年内的序数。
//cPreMonth中分别对应农历的十月、十一月、十二月、正月。
char const cPreMonth[4]={-54,-24,6,35};

//农历月份信息。一年用3个字节表示
//+-----------------------------------------------------------------------+
//| 第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
//保留  1月31日(春节)  闰八月   从左往右依次十二月,十一月……闰八月、八月、七月……正月的天数
//农历月份对应的位为0,表示这个月为29天(小月),为1表示有30天(大月)。
unsigned char const byMonthInfo[60]={
 0x46,0x06,0x93,0x2E,0x95,0x2B,0x54,0x05,0x2B,0x3E,0x0A,0x5B,0x2A,0x55,0x5A, //2000-2004
 0x4E,0x05,0x6A,0x38,0xFB,0x55,0x60,0x0B,0xA4,0x4A,0x0B,0x49,0x32,0xBA,0x93, //2005-2009
 0x58,0x0A,0x95,0x42,0x05,0x2D,0x2C,0x8A,0xAD,0x50,0x0A,0xB5,0x3D,0x35,0xAA, //2010-2014
 0x62,0x05,0xD2,0x4C,0x0D,0xA5,0x36,0xDD,0x4A,0x5C,0x0D,0x4A,0x46,0x0C,0x95  //2015-2019
};

//二十四节气信息。一年用6个字节表示,每个节气使用两位数据。
//+-------------------------------------------------------+
//|  第一字节最高两位  |     第一字节其余6位至第六字节共46个位    |
//|------------------+------------------------------------|
//|  小寒的年内序数减3 |    每个节气距离上一节气的天数,共23组    |
//+-------------------------------------------------------+
//小寒的年内序数已给出,剩下的23个节气分别对应这23组数据,有以下含义:
//+-------------------------------------------------------+
//|  二进制位  | 意义  |                描述                |
//|-----------+------+------------------------------------|
//|     00    | 14天 |    当前对应的节气距离上一节气为14天     |
//|-----------+------+------------------------------------|
//|     01    | 15天 |    当前对应的节气距离上一节气为15天     |
//|-----------+------+------------------------------------|
//|     10    | 16天 |    当前对应的节气距离上一节气为16天     |
//|-----------+------+------------------------------------|
//|     11    | 17天 |    当前对应的节气距离上一节气为17天     |
//+-------------------------------------------------------+
//节气顺序:
//小寒 大寒 立春 雨水 惊蛰 春分 清明 谷雨 立夏 小满 芒种 夏至
//小暑 大暑 立秋 处暑 白露 秋分 寒露 霜降 立冬 小雪 大雪 冬至
//节气数据将由“wSTSource[21]”和“bySTIndex[60]”中的数据生成。
unsigned short const wSTSource[21]={
 0x5156,0x5456,0x5459,0x5525,0x5555,0x5954,0x5A9A,0x6554,0x6555,0x669A, //0-9
 0x66A6,0x69A6,0x8555,0x9156,0x9555,0x9645,0x9651,0x9951,0x99A9,0x9A69, //10-19
 0x9A9A  //20
};

//节气数据的索引表,数据对应“wSTSource[21]”中的索引。
//比如,起始年份(2000年)的节气数据为:wSTSource[bySTIndex[0]],wSTSource[bySTIndex[1]],wSTSource[bySTIndex[2]]这三个16位(6字节)数据。
unsigned char const bySTIndex[60]={
 0x0D,0x09,0x07,0x02,0x0A,0x08,0x04,0x12,0x0E,0x0C,0x13,0x11,0x0D,0x09,0x07, //2000-2004
 0x02,0x0A,0x08,0x04,0x0B,0x0E,0x0C,0x13,0x11,0x0D,0x09,0x05,0x01,0x0A,0x08, //2005-2009
 0x04,0x0B,0x0E,0x0C,0x13,0x10,0x0D,0x06,0x05,0x01,0x09,0x08,0x04,0x0B,0x0E, //2010-2014
 0x0C,0x13,0x0F,0x0C,0x14,0x05,0x00,0x09,0x08,0x03,0x0B,0x0E,0x04,0x12,0x0F  //2015-2019
};

//1999年冬至日在2000年的年内序数,这个数据将被用在2000年“数九”的计算上。
char const cPreDongzhiOrder=-10;

//每年数九、入梅、出梅及三伏信息,一年用两个字节表示。
//+---------------------------------------------------+
//|  第15-11位  |  第10-6位   |  第5-1位   |    最末位   |
//|------------+------------+------------+------------|
//|    入梅     |    出梅     |    初伏    |    末伏     |
//+---------------------------------------------------+
//1.“一九”即是冬至,往后到“九九”的每个“九”相差9天,可顺利推算出来,故“数九”信息省略。
//2.“三伏”天的“中伏”在“初伏”后10天,而“末伏”在“中伏”后10天或20天,因此“中伏”信息省略。
//入梅信息:该天数加上150为入梅这一日的年内序数。
//出梅信息:该天数加上180为出梅这一日的年内序数。
//初伏信息:该天数加上180为初伏这一日的年内序数。
//末伏信息:若该位为“1”,表示末伏距离初伏30天,为“0”表示末伏距离初伏20天。
unsigned short const wExtermSeason[20]={
	0x4359,0x61E1,0x3B97,0x6261,0x3C2A,0x5A9F,0x3468,0x5B1F,0x34E8,0x535D,	//2000-2009
	0x7A26,0x53DD,0x7AA6,0x4C1B,0x72E4,0x4C9B,0x7365,0x41D9,0x6BA3,0x4259 	//2010-2019
};

 

 

 

 

 

 

 

 

如何使用?以下为C版本的核心代码,仅供参考。

 

unsigned short DayOrdinal[13]={0,31,59,90,120,151,181,212,243,273,304,334,365};
unsigned short DayOrdinalLeap[13]={0,31,60,91,121,152,182,213,244,274,305,335,366};

//判断闰年,参数:年份,返回值:0-平年,1-闰年
BOOL IsLeapYear(short sYear);

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

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

//检验年、月、日的合法性,参数:年,月,日,返回值:0-失败,1-成功
BOOL DateCheck(short sYear,unsigned short wMonth,unsigned short wDay);

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

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

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

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

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

//公历转农历,参数:公历年、月、日,农历年、月、日,是否为闰月,返回值:0-失败,1-成功
BOOL Gongli2Nongli(short sYear,unsigned short wMonth,unsigned short wDay,short* sLunarYear,unsigned short* wLunarMonth,unsigned short* wLunarDay,BOOL* bLeapMonth);

//农历转公历,参数:家历年、月、日,是否为闰月,公历年、月、日,返回值:0-失败,1-成功
BOOL Nongli2Gongli(short sLunarYear,unsigned short wLunarMonth,unsigned short wLunarDay,BOOL bLeapMonth,short* sYear,unsigned short* wMonth,unsigned short* wDay);

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

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

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

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

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

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

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

//===============================================================================

BOOL IsLeapYear(short sYear)
{
	if (sYear<1600||sYear>=6400)//压缩算法规定了的年份区间(提取器只能导出此区间的数据,Lunar.dll支持-6000到20000(不含20000)之间的年份)
	{
		return E_FAIL;
	}
	if (sYear%4==0&&sYear%100!=0||sYear%400==0)//判断闰年的条件
	{
		return TRUE;
	}else
	{
		return FALSE;
	}
}

BOOL GetDayOrdinal(short sYear, unsigned short wMonth, unsigned short wDay,int *nDays)
{
	//从日期算出距离元旦的天数
	int ret=0;
	if (DateCheck(sYear,wMonth,wDay)==0)//对输入的日期进行检查
	{
		return 0;
	}
	ret=IsLeapYear(sYear);//判断是否为闰年
	if (ret==-1)
	{
		return 0;
	}
	if (ret==1)
	{
		*nDays=DayOrdinalLeap[wMonth-1]+wDay-1;//元旦为序数0,因此减1
	}else
	{
		*nDays=DayOrdinal[wMonth-1]+wDay-1;
	}
	return 1;
}

BOOL GetDateFromOrdinal(short sYear,short sOrdinal,unsigned short* wMonth,unsigned short* wDay)
{
	//从年内序数计算日期
	int ret=0,i=0;
	if (sOrdinal<0)
	{
		return FALSE;
	}
	ret=IsLeapYear(sYear);
	if (ret==1)
	{
		if (sOrdinal>=366)//超出了该年的总天数
		{
			return FALSE;
		}
	}else
	{
		if (sOrdinal>=365)
		{
			return FALSE;
		}
	}
	if (ret!=-1)
	{
		//年符合,则计算;
		*wMonth=0;
		for (i=0;i<12;i++)
		{
			if (ret==1)
			{
				if (sOrdinal>=DayOrdinalLeap[i]&&sOrdinal=DayOrdinal[i]&&sOrdinal=END_YEAR||wMonth>12||wMonth<1||wDay<1||wDay>31)
	{
		return 0;
	}
	if (wMonth==4||wMonth==6||wMonth==9||wMonth==11)
	{
		if (wDay==31)
		{
			return 0;
		}
	}else if (wMonth==2)
	{
		if (IsLeapYear(sYear)==0)
		{
			if (wDay>28)
			{
				return 0;
			}
		}else
		{
			if (wDay>29)
			{
				return 0;
			}
		}
	}
	return 1;
}

short LunarGetNewYearOrdinal(short sLunarYear)
{
	unsigned int uData=0;
	if (GetMonthInfo(sLunarYear,&uData)==FALSE)
	{
		return 0;
	}
	return (short)((uData>>17)&0x3F);//取出农历新年的年内序数
}

unsigned short LunarGetDaysofMonth(short sLunarYear,unsigned short wLunarMonth,BOOL bLeapMonth)
{
	int i=0;//循环变量
	unsigned int DX_data=0;//该年大小月情况
	unsigned int uData=0;
	int Acc_LeapMonth=0;
	if (sLunarYear==START_YEAR-1)//农历还在起始年份的前一年
	{
		if (cPreLeapIndex==-1)//无闰月
		{
			if (bLeapMonth==TRUE)
			{
				return 0;
			}
			return cPreMonth[wLunarMonth-9]-cPreMonth[wLunarMonth-10];
		}else
		{
			Acc_LeapMonth=cPreLeapIndex+9;
			if (Acc_LeapMonth>wLunarMonth)
			{
				if (bLeapMonth==TRUE)
				{
					return 0;
				}
				return cPreMonth[wLunarMonth-9]-cPreMonth[wLunarMonth-10];
			}else
			{
				if ((bLeapMonth==TRUE&&wLunarMonth==Acc_LeapMonth)||wLunarMonth>Acc_LeapMonth)
				{
					return cPreMonth[wLunarMonth-8]-cPreMonth[wLunarMonth-9];
				}else
				{
					return cPreMonth[wLunarMonth-9]-cPreMonth[wLunarMonth-10];
				}
			}
		}
	}
	if (GetMonthInfo(sLunarYear,&uData)==FALSE)
	{
		return 0;
	}
	DX_data=(unsigned short)(uData&0x1FFF);//整年大小月情况
	Acc_LeapMonth=LunarGetLeapMonth(sLunarYear);//获取真实闰月月份
	if (bLeapMonth)//指定查询的当前月是闰月
	{
		if (Acc_LeapMonth!=wLunarMonth)
		{
			return 0;//不存在的闰月
		}
		for (i=0;i>=1;//右移一位,即从末尾开始找该闰月月份所在的位
		}
	}else
	{
		if (Acc_LeapMonth>0)//存在闰月
		{
			if (wLunarMonth<=Acc_LeapMonth)//月份在闰月之前,倒找需要多找一位
			{
				for (i=0;i>=1;
				}
			}else
			{
				for (i=0;i>=1;
				}
			}
		}else
		{
			for (i=0;i>=1;
			}
		}
	}
	if (DX_data&0x1)
	{
		return 30;//大月
	}else
	{
		return 29;//小月
	}
}

BOOL LunarExpandDX(short sLunarYear,int iDayOfMonth[15])
{
	int i=0,pos=0,iLeapMonth=0;//循环变量,数组写入位置,闰月
	if (sLunarYear=END_YEAR)
	{
		return FALSE;
	}
	for (i=0;i<15;i++)
	{
		iDayOfMonth[i]=0;//对数组清零
	}
	if (sLunarYear==START_YEAR)
	{
		if (cPreLeapIndex==-1)//处理起始年份之前一年数据
		{
			iDayOfMonth[pos]=cPreMonth[2]-cPreMonth[1];//农历上一年十一月总天数
			pos++;
		}else
		{
			if (cPreLeapIndex==1)//闰十月
			{
				iDayOfMonth[pos]=cPreMonth[3]-cPreMonth[2];
				pos++;
			}else
			{
				//闰十一月或闰十二月
				iDayOfMonth[pos]=cPreMonth[2]-cPreMonth[1];
				pos++;
				iDayOfMonth[pos]=cPreMonth[3]-cPreMonth[2];
				pos++;
			}
		}
		iDayOfMonth[pos]=LunarGetNewYearOrdinal(sLunarYear)-cPreMonth[2];//(闰)十二月
		pos++;
	}else
	{
		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;
}

BOOL GetMonthInfo(unsigned short wYear,unsigned int* puData)
{
	int iStartPos=(wYear-START_YEAR)*3;
	unsigned int uData=0;
	if (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;
}

unsigned short LunarGetLeapMonth(short sLunarYear)
{
	unsigned int data=0;
	unsigned short wLeapMonth=0;
	if (GetMonthInfo(sLunarYear,&data)==FALSE)
	{
		return 0;
	}
	wLeapMonth=(unsigned short)((data>>13)&0x0F);
	return wLeapMonth;
}

BOOL Gongli2Nongli(short sYear,unsigned short wMonth,unsigned short wDay,short* sLunarYear,unsigned short* wLunarMonth,unsigned short* 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=END_YEAR||wLunarMonth<1||wLunarMonth>12||wLunarDay<1||wLunarDay>30)
	{
		return 0;//年、月、日检查
	}
	if (bLeapMonth)
	{
		if (LunarGetLeapMonth(sLunarYear)!=wLunarMonth)
		{
			return FALSE;//闰月检查
		}
	}
	if (wLunarDay>LunarGetDaysofMonth(sLunarYear,wLunarMonth,bLeapMonth))
	{
		return FALSE;//大小月检查
	}
	LunarExpandDX(sLunarYear,DaysOfLunarMonth);//大小月表(农历每月天数)
	LunarNewYear=LunarGetNewYearOrdinal(sLunarYear);//找到正月初一r公历年内序数
	iLeapMonth=LunarGetLeapMonth(sLunarYear);//找出农历年的闰月
	remain_days+=LunarNewYear;//加上正月初一的序数
	if (iLeapMonthPre>10)
	{
		i=3;//今年正月在“DaysOfLunarMonth”中的索引,上一年十一月或十二月有闰月
	}else
	{
		i=2;//上一年十一月和十二月无闰月时,今年正月的索引
	}
	if (iLeapMonth==0)
	{
		if (iLeapMonthPre>10)
		{
			for (;iiDaysofYear)
	{
		remain_days-=iDaysofYear;
		*sYear+=1;//下一年
	}
	GetDateFromOrdinal(*sYear,remain_days,wMonth,wDay);
	return TRUE;
}

BOOL GetJieQi(short sYear,unsigned short wMonth,unsigned short wJieQi[2])
{
	int index=0;//对应每公历年首个节气所在字节的索引
	unsigned short wXiaoHanOrder=0;//小寒年内序数
	unsigned short wJQData=0;//节气数据
	unsigned short wCurJQData=0;//当前计算的节气数据
	unsigned short wDays=0;//当前节气距离该年小寒的天数
	int i=0;
	int iRem=0;
	if (sYear=END_YEAR||wMonth>12||wMonth<1)
	{
		return FALSE;
	}
	index=(sYear-START_YEAR)*3;//对应每公历年首个节气所在字节的索引
 	wJQData=wSTSource[bySTIndex[index]];
 	wXiaoHanOrder=(unsigned short)((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>(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;
}

unsigned short GetDayOfWeek(short sYear,unsigned short wMonth,unsigned short wDay)
{
	unsigned 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;
}

unsigned short GetDaysOfMonth(short sYear,unsigned short 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;
}

BOOL GetExtremeSeason(short sYear,short* sYijiu,unsigned short* wChuFu,unsigned short* wMoFu)
{
	unsigned short wjq[2]={0};
	int ET_index=sYear-START_YEAR;//数九、梅雨及三伏的年份索引
	if (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;
}

BOOL GetMeiYu(short sYear,unsigned short* wRuMeiOrd,unsigned short* wChuMeiOrd)
{
	int ET_index=sYear-START_YEAR;//数九、梅雨及三伏的年份索引
	if (sYear=END_YEAR)
	{
		return FALSE;
	}
	*wRuMeiOrd=((wExtermSeason[ET_index]&0xF800)>>11)+150;
	*wChuMeiOrd=((wExtermSeason[ET_index]&0x07C0)>>6)+180;
	return TRUE;
}

 

 

 

 

 

 

CSDN下载:源代码以及提取工具

 

 





你可能感兴趣的:(农历相关)