分享
 
 
 

公历农历相互转换的算法及其VCL实现

王朝厨房·作者佚名  2007-01-04
窄屏简体版  字體: |||超大  

抱雪

你到过我的主页吗?在我的主页上有这样一个地方:

你注意到了吗,在显示时间的地方除了显示公历之外,还显示了农历:农历辛已(蛇)年二月廿三日未时,比一般的网站上只显示公历就酷多了(怎么像自吹自擂?别的网站千万别去告我违反了广告法)。这是怎么做的呢?其实很简单,只要一个小小的PHP或者JavaScript程序就可以了。

你不要着急地问我要PHP或JS的程序,最关键的是要了解算法,如果你明白了转换的道理,就可以达到圣人所说的:举一而三反焉,到时不管是用PHP、DELPHI、JS还是JSP、VB,你都可以很快地写公历农历相互转换的程序来出来。我记得有高人曾经说过,编程语言只是工具,数据结构才是最重要的,此言诚不虚也。

闲话少说,下面我就来介绍一下具体的算法。

首先是要保存公农历之间的转换信息:以任意一年作为起点,把从这一年起若干年(若干是多少?就看你的需要了)的农历信息保存起来(在我的VCL中,是以1921年作为起点)。回想一下,我们平常是怎样来转换公历农历的呢?是查万年历,万年历有每一天的公历农历,直接一查就可以了。那么我们可不可以也这样做呢?当然可以,但是,这样做就要收录每一天的信息,工作量就会很大,所以我们要简化这些信息。怎么简化呢?要保存一年的信息其实只要两个信息就可以了:1、农历每个月的大小;2、今年是否有闰月,闰几月以及闰月的大小。用一个整数来保存这些信息就足够了。具体的方法是:用一位来表示一个月的大小,大月记为1,小月记为0,这样就用掉12位(无闰月)或13位(有闰月),再用高4位来表示闰月的月份,没有闰月记为0。比如说,2000年的信息数据是是0xC96,化成二进制就是110010010110B,表示的含义是指1、2、5、8、10、11月大,其余月小;2001年的农历信息数据是0x41A95,其中4表示今年闰四月,月份大小信息就是0x1A95(因为闰月,所以有13位),具体的就是1、2、4、5、8、10、12月大,其余月份小(0x1A95=1101010010101B),要注意在四月的后面那一个0表示的是闰四月小,接着的那个1表示5月大。这样就可以用一个数组来保存这些信息。在我的VCL程序中是用ChineseCalendarData[]这个数组来保存这些信息。

为了方便对算法的理解,首先来看看我的VCL组件hsDivineCalendar的头文件

//---------------------------------------------------------------------------

#ifndef hsDivineCalendarH

#define hsDivineCalendarH

#define ALLYEARS 100 //定义转换的年数:100年

//---------------------------------------------------------------------------

#include <SysUtils.hpp>

#include <Controls.hpp>

#include <Classes.hpp>

#include <Forms.hpp>

//---------------------------------------------------------------------------

class PACKAGE ThsDivineCalendar : public TComponent

{

private:

int ChineseCalendarData[ALLYEARS]; //农历数据

AnsiString str2,num; //要用的字符串

void __fastcall c2e(); //农历到公历

void __fastcall e2c(); //公历到农历

TDateTime TheDate; //日期

int FYear,FMonth,FDay,FTime; //公历年月日时

int FcYear,FcMonth,FcDay,FcTime; //农历年月日时

AnsiString __fastcall GetDateString(); //获取日期字符串

AnsiString __fastcall GetcDateString(); //获取农历日期字符串

int __fastcall GetBit(int m,int n); //获取1bit

void __fastcall GetYMD(); //获取年月日

void __fastcall SetDate(TDateTime); //用一个TDateTime类型的变量转换

//-----------分别修改公历的年月日时-------------------------------------

void __fastcall SetYear(int AYear){SetBy(AYear,FMonth,FDay,FTime);};

void __fastcall SetMonth(int AMonth){SetBy(FYear,AMonth,FDay,FTime);};

void __fastcall SetDay(int ADay){SetBy(FYear,FMonth,ADay,FTime);};

void __fastcall SetTime(int ATime){SetBy(FYear,FMonth,FDay,ATime);};

//-----------分别修改农历的年月日时---------------------------------------

void __fastcall SetcYear(int AcYear){SetByc(AcYear,FcMonth,FcDay,FcTime);};

void __fastcall SetcMonth(int AcMonth){SetByc(FcYear,AcMonth,FcDay,FcTime);};

void __fastcall SetcDay(int AcDay){SetByc(FcYear,FcMonth,AcDay,FcTime);};

void __fastcall SetcTime(int AcTime){SetByc(FcYear,FcMonth,FcDay,AcTime);};

TDateTime __fastcall GetLastJie(); //取得上一个节

TDateTime __fastcall GetNextJie(); //取得下一个节

TDateTime __fastcall GetLastQi(); //取得上一个中气

TDateTime __fastcall GetNextQi(); //取得下一个中气

int __fastcall GetDayOfWeek(); //取得一周的第几天

AnsiString __fastcall GetWeekString(); //返回星期几的字符串

protected:

public:

void __fastcall SetBy(int,int,int,int); //用公历设置

void __fastcall SetByc(int,int,int,int); //用农历设置

__fastcall ThsDivineCalendar(TComponent* Owner);

//属性:年月日时

__property int Year={read=FYear,write=SetYear};

__property int Month={read=FMonth,write=SetMonth};

__property int Day={read=FDay,write=SetDay};

__property int Time={read=FTime,write=SetTime};

//属性:农历年月日时

__property int cYear={read=FcYear,write=SetcYear};

__property int cMonth={read=FcMonth,write=SetcMonth};

__property int cDay={read=FcDay,write=SetcDay};

__property int cTime={read=FcTime,write=SetcTime};

//公历农历日期字符串

__property AnsiString DateString={read=GetDateString};

__property AnsiString cDateString={read=GetcDateString};

//其他属性

__property TDateTime DateTime={read=TheDate,write=SetDate};

__property TDateTime LastJie = { read=GetLastJie };

__property TDateTime NextJie = { read=GetNextJie };

__property TDateTime LastQi = { read=GetLastQi };

__property TDateTime NextQi = { read=GetNextQi };

__property int DayOfWeek = { read=GetDayOfWeek };

__property AnsiString WeekString = { read=GetWeekString };

__published:

};

//---------------------------------------------------------------------------

#endif

下面介绍转换的具体算法。

一、公历转换成农历

1、计算出所求时间到起始年正月初一的天数。

2、从起始年份开始,减去每一月的天数,一直到剩余天数没有下一个月多为止

此时,ChineseCalendarData[]的下标到了多少,就是减去了多少年,用起始年份加上这个下标就可以得到农历年份;然后看减去了几个月,如果本年不闰月或者闰月还在后面,就可以直接得到农历月份,如果在闰月月份数后面一个月,则这个月就是闰月,如果在闰月的后面,则要减去1才能得到月份数;剩余的天数就是农历日;农历时用(公历时+1)/2就可以简单地得到了。具体的代码如下:

//---------------------------------------------------------------------------

void __fastcall ThsDivineCalendar::e2c()

{

int total,m,n,k;

bool isEnd=false; //用以判断是否不够减了

total=(int)TheDate-7709; //到1921-2-8(正月初一)的天数

for(m=0;;m++)

{

/*判断本年是否闰月,用以确定月份信息的起点

有闰月有13位(0~12),无12位(0~11)*/

k=(ChineseCalendarData[m]<0xfff)?11:12; for(n=k;n>=0;n--)

{

//如果不够减

if(total<=29+GetBit(ChineseCalendarData[m],n))

{

isEnd=true; //设置标志

break; //退出内层循环

}

/*够减,减去一个月的天数

先减去29天如果月大,则对应的信息位为1,

又减去一天*/

total=total-29-GetBit(ChineseCalendarData[m],n);

}

if(isEnd)break; //如果不够减,退出外层循环

}

FcYear=1921 + m; //农历年=起始年份+下标

FcMonth=k-n+1; //农历月=本年的月份数(k+1)减去已经减去的月份数(n)

FcDay=total; //农历日=剩余天数

unsigned short int t1,t2,t3,t4;

TheDate.DecodeTime(&t1,&t2,&t3,&t4);

FcTime=(t1+1)>>1; //农历时

if(k==12) //如果本年有闰月

{

if(FcMonth==ChineseCalendarData[m]/0x10000+1)//就是闰月

FcMonth=1-FcMonth;

if(FcMonth>ChineseCalendarData[m]/0x10000+1)//闰月后面

FcMonth--;

}

}

//----------------------------------------------------------------------------

二、农历到公历的转换

这个算法比较简单,只要计算所求时候到起始年正月初一的总天数就可以了,要计算总天数,只要统计出本月以前的大月小月书就可以了,然后把这个值赋予TdateTime类型的TheDate就可以用TdateTime的成员函数DecodeDate得到公历的年月日了。具体代码如下:

//----------------------------------------------------------------------------

void __fastcall ThsDivineCalendar::c2e()

{

int i,k,m,p,y[]={0,0};

//y[0]:小月、y[1]:大月

//本年以前的大月小月数

for(i=0;i<FcYear-1921;i++)

{

k=(ChineseCalendarData[i]<0xfff)?11:12;

for(m=0;m<=k;m++)

y[GetBit(ChineseCalendarData[i],m)]++;

}

//统计本年本月以前的大月小月数

//本年不是闰年

if(ChineseCalendarData[i]<0xfff)

for(m=13-FcMonth;m<=11;m++)

y[GetBit(ChineseCalendarData[i],m)]++;

else // 是闰年

{

k=ChineseCalendarData[i]/0x10000;

//根据在闰月前后决定统计的起始位置

p=(FcMonth>k)?13-FcMonth:14-FcMonth;

if(k+FcMonth==0)p=13+FcMonth; //本月就是闰月

for(m=p;m<=12;m++)

y[GetBit(ChineseCalendarData[i],m)]++;

}

//7709就是1920年腊月三十

TheDate=7709+y[0]*29+y[1]*30+FcDay+FcTime*2.0/24;

}

//----------------------------------------------------------------------------

void __fastcall ThsDivineCalendar::GetYMD()

{

unsigned short y,m,d,t;

TheDate.DecodeDate(&y,&m,&d);

FYear=y;

FMonth=m;

FDay=d;

TheDate.DecodeTime(&t,&y,&m,&d);

FTime=t;

}

//----------------------------------------------------------------------------

以上就是公历农历相互转换的算法和VCL代码,只要理解了这些算法,你就不难写出其他的程序,我就写了JavaScript和PHP的代码,其实PHP、JS的代码简单得多,只需要有公历到农历的转换就可以了。如果你想要这些代码和完整的VCL源代码,你就说嘛,你不说我怎么知道你想要呢?虽然你很有诚意地看着我……,哈哈,我又中《大话西游》的毒了:=)。其实,这些代码你可以在《电脑爱好者》网站或者我的主页(http://bcbtop.126.com)的主页下载。

http://hugsnow.myetang.com/source/hugsnow1.zip

 
 
 
免责声明:本文为网络用户发布,其观点仅代表作者个人观点,与本站无关,本站仅提供信息存储服务。文中陈述内容未经本站证实,其真实性、完整性、及时性本站不作任何保证或承诺,请读者仅作参考,并请自行核实相关内容。
2023年上半年GDP全球前十五强
 百态   2023-10-24
美众议院议长启动对拜登的弹劾调查
 百态   2023-09-13
上海、济南、武汉等多地出现不明坠落物
 探索   2023-09-06
印度或要将国名改为“巴拉特”
 百态   2023-09-06
男子为女友送行,买票不登机被捕
 百态   2023-08-20
手机地震预警功能怎么开?
 干货   2023-08-06
女子4年卖2套房花700多万做美容:不但没变美脸,面部还出现变形
 百态   2023-08-04
住户一楼被水淹 还冲来8头猪
 百态   2023-07-31
女子体内爬出大量瓜子状活虫
 百态   2023-07-25
地球连续35年收到神秘规律性信号,网友:不要回答!
 探索   2023-07-21
全球镓价格本周大涨27%
 探索   2023-07-09
钱都流向了那些不缺钱的人,苦都留给了能吃苦的人
 探索   2023-07-02
倩女手游刀客魅者强控制(强混乱强眩晕强睡眠)和对应控制抗性的关系
 百态   2020-08-20
美国5月9日最新疫情:美国确诊人数突破131万
 百态   2020-05-09
荷兰政府宣布将集体辞职
 干货   2020-04-30
倩女幽魂手游师徒任务情义春秋猜成语答案逍遥观:鹏程万里
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案神机营:射石饮羽
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案昆仑山:拔刀相助
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案天工阁:鬼斧神工
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案丝路古道:单枪匹马
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案镇郊荒野:与虎谋皮
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案镇郊荒野:李代桃僵
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案镇郊荒野:指鹿为马
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案金陵:小鸟依人
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案金陵:千金买邻
 干货   2019-11-12
 
推荐阅读
 
 
 
>>返回首頁<<
 
靜靜地坐在廢墟上,四周的荒凉一望無際,忽然覺得,淒涼也很美
© 2005- 王朝網路 版權所有