Python实现150年公历农历换算

整理文档很辛苦,赏杯茶钱您下走!

免费阅读已结束,点击下载阅读编辑剩下 ...

阅读已结束,您可以下载文档离线阅读编辑

资源描述

Python实现150年公历农历换算烤鱼片cleverysm@gmail.com前段时间玩twitter,跟风做了一个自动报时的机器人。一来练习python,而来玩玩gae,看看twitter的api是什么样子的。单是报公历太普通不好玩,于是想来做个报农历时间的,于是研究了一下农历的换算问题,并用python做了个小程序实现从1901年到2050年的换算。要实现这个功能首先是要搞明白农历历法的规律。以前没查过这方面材料,以为是有什么公式可以直接从公历时间换算一下就可以,其实不然,中国农历的推算那可是相当复杂的,精确计算是得用上天体力学的。首先要纠正一个不准确的说法,就是阴历。我们用的农历历法民间也俗称阴历,对应公元纪年的叫做阳历。顾名思义,阳历就是根据太阳与地球的相对运动关系计算的立法,阴历则是根据月亮周期计算的。乍看我们的农历好似是阴历,一个月恰是一个月亮的周期,但实际上农历还要根据太阳周期对一年的日期进行矫正,否则要是简单按照月亮周期计算的话一年就不是365天,会跟阳历有固定的误差,时间久了误差就会越来越大。为了避免这种误差,保证阴历一年和阳历一年基本对应,就引入了矫正机制,也就是闰月。而24节气虽是农历节气,但其时间则是根据地球相对太阳公转的相对位置确定的,而不是固定在某个阴历日期。因此我们所说的阴历其实应该算是一种阴阳混合历,真正典型的阴历代表则是伊斯兰历。农历的计算就要涉及到每年是否闰月,闰几月,每个月有多少天,也就是大小月问题,二十四节气日期的确定这几个关键数据,这些都要用到太阳月亮地球三者之间的运行状况的计算,非专业人士很难搞的明白,而作为一般应用,也只有一种比较好的办法,那就是查表,找到一些关键时间数据存下来,计算的时候根据这些数据进行计算。那么,公历到农历的转化需要最终得到哪些信息呢?首先第一个是农历的年份,也就是每年哪一天是大年初一,这个日子的确定取决于每年的闰月情况和各个月的大小月分布;然后是农历的月份和日期,也就是要算出今天是农历的几月几号;所计算日期是否属于24节气,是哪个节气;以及年干支、月干支、日干支乃至某时刻的干支是什么都要算出来。我们先从简单的开始算起。1.时刻干支时刻干支简单的将就是在古装剧上常听到“子时”“午时”来表示一天的时刻,12个地支一天,也就是一个地支是两个小时,子时就是从23点到第二天的1点间俩小时,其他的类推下去。如果再精确点还可以在地支前加一个天干,也就是每个时刻可以表示成“甲子”“乙丑”这样的。地支时刻很简单,就是一天对应12个,直接对应就行。天干是10个,和地支组合起来则是60一循环,不能跟一天的12个时辰直接对应,所以就要取一个基准时刻,知道那个时刻的天干地支就可以根据60一循环的规律进行推算了。在这里我以1901年的1月1日凌成1点为基准点,此刻是乙丑时的开始,函数只做了向后计算,向前推算也很简单,稍改一下就可以。这里为最终返回干支结果,定义了几个常量,对应干支的汉字。TIAN_GAN=(甲,乙,丙,丁,戊,己,庚,辛,壬,癸)DI_ZHI=(子,丑,寅,卯,辰,巳,午,未,申,酉,戌,亥)HOUR_GANZHI={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:子时}defGetHourGanzhi1901(arg):#以1901年1月1日凌晨一点为基准点startdatetime=datetime.datetime(year=1901,month=1,day=1,hour=1)#60干支乙丑是第二个,以0为起点,则编号为1startganzhi=1ifnotisinstance(arg,datetime.datetime):return#计算离基准时刻过去了多少时间delta=arg-startdatetimeifdelta.seconds0:return#计算时刻的干支编号hour=delta.days*24+delta.seconds/3600num=(startganzhi+hour/2)%10returnTIAN_GAN[num]+HOUR_GANZHI[arg.hour]2.日干支日干支就是每天对应的天干地支表示,基本做法与时刻干支类似,也是以某一确定基准日期为起点,根据时间差来计算。这里同样也是以1901年1月1日为基准点,当天是乙卯日。defGetDayGanzhiNum1901(arg):#函数返回60干支编号,编号范围[1,60]ifnotisinstance(arg,datetime.date):return0startdate=datetime.date(1901,1,1)startganzhi=16ifnotisinstance(arg,datetime.date):return0delta=arg-startdateifdelta.days+delta.seconds0:returnnum=(startganzhi+delta.days)%60ifnum==0:num=60returnnum获得编号后如需获取汉字表示,可以再写一个函数来做这种转换:defNum2Ganzhi(arg):#arg范围是[1,60]ifnotarginrange(1,61):returnx=(arg-1)%10y=(arg-1)%12returnTIAN_GAN[x]+DI_ZHI[y]3.农历年份农历年份的确定实际上就是要设法知道每年年初一在公历的哪天,上面提到这涉及到每年的闰月和大小月分布,如果不用复杂的天文计算的话只能是用查表的方式解决。我用的数据是从网上搜来的代码里的,是一位信箱是wangfei@hanwang.com.cn的朋友整理出来的,数据时间从1901年到2050年,共150年,代码中原作者表示如需使用他整理出来的数据和代码的话须注明这一情况,本着尊重原作者劳动的原则,在此向此为朋友表示感谢。原作者代码是用于嵌入式设备的,所以对数据采取了特别的存储方式以减小内存使用量。比如说从1901到1910年的闰月的月份采取如下存储方式,如没有闰月则为0,每字节存两年。0x00,0x50,0x04,0x00,0x20,#1910,这样不直观,所以我对这种数据做了展开处理,成为1901:0,1902:0如此形式的数据,表示每一年闰月月份,更加直观,易于阅读。为了计算农历年份,我做了一个150年间每年正月初一对应与阳历年第几天的表。YEAR_FIRST_DAY={1901:50,1902:39,1903:29,1904:47,...}借用此表就可以计算此150年间每一天对应是哪一个农历年份了。为了方便管理,以下的功能都写到一个ChineseCalendar150类中。其中获取农历年份的函数部分代码如下所示:def__GetChineseYearNum(self,arg=None):将公立日期转换为农历年份#计算是这一年的第几天lastyear1231=datetime.date(arg.year-1,month=12,day=31)delta=arg-lastyear1231year=1901ifdelta.days=self.YEAR_FIRST_DAY[arg.year]:year=arg.yearelse:year=arg.year-1returnyear4.农历日期农历日期涉及因素比较多一点,计算这个首先需要农历年份,这点好办,上一步已经做出来了,然后还需要当年闰月情况,以及每个月的大小月情况。大小月指的是农历月有的是30天,有的是29天,这个天数不像公历那么固定有规律,它是为了修正月亮周期不能整除地球天而来的,所以得用对应表记下来。MONTH_DAY={1901:[29,30,29,29,30,29,30,29,30,30,30,29,0],...}每年用13个数字记录每月的天数,最后一个是0表明当年没闰月。闰月情况也同样是要建表存储。RUN_YUE={1901:0,1902:0,1903:5,1904:0,1905:0,1906:4,1907:0,1908:0,1909:2,1910:0,...}每年年号对应数字为闰月月份,0表示当年没有闰月。这样,只要通过公历日期就可以得到所属农历年份,通过农历年份就知道年初一是公历哪一天,进而得知当天是农历年中的第几天,然后再根据当年闰月及大小月情况推算出当日是农历的哪一月的第几天了。def__GetChineseDate(self,arg=None):获取某日期的农历日期,返回(是否大月,是否闰月,月份,日子)dayue={30:True,29:False}#获取农历年份yearnum=self.GetChineseYearNum(arg)#计算当天是农历年的第几天delta=datetime.timedelta(days=self.YEAR_FIRST_DAY[yearnum]-1)gongliyuandan=datetime.date(year=yearnum,month=1,day=1)gonglichiyi=gongliyuandan+deltadelta=arg-gonglichiyidays=delta.days+1month=[]ifself.RUN_YUE[yearnum]0:foriinrange(13):ifi+1=self.RUN_YUE[yearnum]:month.append([False,i+1,self.MONTH_DAY[yearnum][i]])elifi==self.RUN_YUE[yearnum]:month.append([True,i,self.MONTH_DAY[yearnum][i]])else:month.append([False,i,self.MONTH_DAY[yearnum][i]])passelse:foriinrange(12):month.append([False,i+1,self.MONTH_DAY[yearnum][i]])foriinrange(len(month)):days-=month[i][2]ifdays=0:return(dayue[month[i][2]],month[i][0],month[i][1],days+month[i][2])5.年干支年干支就是用天干地支来标记年份,比如2010年就是庚寅虎年,天干地支60年一轮回不停的循环,进而年干支的计算也就还是找个基准点,计算相差年份。但这里有个问题,就是年干支的更替时刻是什么时候。现在我们一般是将这个时刻定在农历正月初一的凌成0时,也就是春晚敲钟的那一刻。这一刻起,农历年干支就要更换,进而属相也就更换。但实际上在过去传统立法中是以立春算起的,或者更严格来讲是从立春的那一时刻开始更替年干支的。这样就会出现一个有意思的情况,就是如果两个人出生在同一年的立春那天,但是一个是在立春那刻前出生,另一个在之后出生,他们按照传统历法虽然同一天出生,但属相却不同。(说到这,我想到一个问题,那就是可能很多朋友搞不清楚属相怎么算,还以为是从元旦那天所起,那更是大错特错了)我们就不那么考究了,不去考虑那么精确的时刻问题,还是以天为整体单位吧,首先按照正月初一更换年干支来计算,后面再补充一个扩展类来实现立春更换年干支功能。(其实初一更换年干支也有点问题,那就是到底是从零点算起呢还是从子时也就是前一天23点算起呢,呵呵,不考虑那么多了)我们的基准年份是1901年,当年大年初一是公历的2月19日,部分代码如下。def__GetYearGanzhiNum(self,arg=None):获取干支年序号[1,60]year=self.GetChineseYearNum(arg)num=((year-self.START_DATE.year)+Ganzh

1 / 7
下载文档,编辑使用

©2015-2020 m.777doc.com 三七文档.

备案号:鲁ICP备2024069028号-1 客服联系 QQ:2149211541

×
保存成功