设为首页 收藏本站
查看: 1549|回复: 0

[经验分享] Python农历公历转换

[复制链接]

尚未签到

发表于 2017-4-29 13:43:22 | 显示全部楼层 |阅读模式
  实现中主要有两个方法,一个是get_lunar_date,它根据公历返回农历,返回是一个
四元组(year,month,day,leap),leap为True表示返回的月份在闰月,为False时表示不是闰月。另外一个是
get_solar_date,它根据农历返回公历,由于农历存在闰月,可能有一个农历日期对应着两个公历日期,这个方法返回一个数组,如果农历月份为闰
月,这个数组会包含两个公历日期,否则只有一个公历日期。这个方法对日期校验并不十分严格,它允许任意月份的天数为30,严格说来某些月份只有29天,例
如农历2009年10月只有29天,但get_solar_date也可以接收2009年10月30,即
get_solar_date(2009,10,30),它相当于返回农历2009年11月1日的公历日期。另外,严格地说农历2009年5月30号只对应着一个公历日期,因为虽然2009年的5月是闰月,存在两个5月,但只有一个5月有30号,但该程序会返回两个公历日期。
  源代码如下:

#!/usr/bin/env python
#coding=utf-8
# 存储1901-2099年每年每月的天数,第1位到第13位存储每月(包括闰月共13月)的天数,为1表示该月为30天,
# 为0表示该月为29天。第12-15位表示该年闰月的月份,如果为0x0F表示该年没有闰月。
g_lunar_month_days = [
0xF0EA4, 0xF1D4A, 0x52C94, 0xF0C96, 0xF1536, 0x42AAC, 0xF0AD4, 0xF16B2, 0x22EA4, 0xF0EA4,  # 1901-1910
0x6364A, 0xF164A, 0xF1496, 0x52956, 0xF055A, 0xF0AD6, 0x216D2, 0xF1B52, 0x73B24, 0xF1D24,  # 1911-1920
0xF1A4A, 0x5349A, 0xF14AC, 0xF056C, 0x42B6A, 0xF0DA8, 0xF1D52, 0x23D24, 0xF1D24, 0x61A4C,  # 1921-1930
0xF0A56, 0xF14AE, 0x5256C, 0xF16B4, 0xF0DA8, 0x31D92, 0xF0E92, 0x72D26, 0xF1526, 0xF0A56,  # 1931-1940
0x614B6, 0xF155A, 0xF0AD4, 0x436AA, 0xF1748, 0xF1692, 0x23526, 0xF152A, 0x72A5A, 0xF0A6C,  # 1941-1950
0xF155A, 0x52B54, 0xF0B64, 0xF1B4A, 0x33A94, 0xF1A94, 0x8152A, 0xF152E, 0xF0AAC, 0x6156A,  # 1951-1960
0xF15AA, 0xF0DA4, 0x41D4A, 0xF1D4A, 0xF0C94, 0x3192E, 0xF1536, 0x72AB4, 0xF0AD4, 0xF16D2,  # 1961-1970
0x52EA4, 0xF16A4, 0xF164A, 0x42C96, 0xF1496, 0x82956, 0xF055A, 0xF0ADA, 0x616D2, 0xF1B52,  # 1971-1980
0xF1B24, 0x43A4A, 0xF1A4A, 0xA349A, 0xF14AC, 0xF056C, 0x60B6A, 0xF0DAA, 0xF1D92, 0x53D24,  # 1981-1990
0xF1D24, 0xF1A4C, 0x314AC, 0xF14AE, 0x829AC, 0xF06B4, 0xF0DAA, 0x52D92, 0xF0E92, 0xF0D26,  # 1991-2000
0x42A56, 0xF0A56, 0xF14B6, 0x22AB4, 0xF0AD4, 0x736AA, 0xF1748, 0xF1692, 0x53526, 0xF152A,  # 2001-2010
0xF0A5A, 0x4155A, 0xF156A, 0x92B54, 0xF0BA4, 0xF1B4A, 0x63A94, 0xF1A94, 0xF192A, 0x42A5C,  # 2011-2020
0xF0AAC, 0xF156A, 0x22B64, 0xF0DA4, 0x61D52, 0xF0E4A, 0xF0C96, 0x5192E, 0xF1956, 0xF0AB4,  # 2021-2030
0x315AC, 0xF16D2, 0xB2EA4, 0xF16A4, 0xF164A, 0x63496, 0xF1496, 0xF0956, 0x50AB6, 0xF0B5A,  # 2031-2040
0xF16D4, 0x236A4, 0xF1B24, 0x73A4A, 0xF1A4A, 0xF14AA, 0x5295A, 0xF096C, 0xF0B6A, 0x31B54,  # 2041-2050
0xF1D92, 0x83D24, 0xF1D24, 0xF1A4C, 0x614AC, 0xF14AE, 0xF09AC, 0x40DAA, 0xF0EAA, 0xF0E92,  # 2051-2060
0x31D26, 0xF0D26, 0x72A56, 0xF0A56, 0xF14B6, 0x52AB4, 0xF0AD4, 0xF16CA, 0x42E94, 0xF1694,  # 2061-2070
0x8352A, 0xF152A, 0xF0A5A, 0x6155A, 0xF156A, 0xF0B54, 0x4174A, 0xF1B4A, 0xF1A94, 0x3392A,  # 2071-2080
0xF192C, 0x7329C, 0xF0AAC, 0xF156A, 0x52B64, 0xF0DA4, 0xF1D4A, 0x41C94, 0xF0C96, 0x8192E,  # 2081-2090
0xF0956, 0xF0AB6, 0x615AC, 0xF16D4, 0xF0EA4, 0x42E4A, 0xF164A, 0xF1516, 0x22936,           # 2090-2099
]
import sys
from datetime import datetime, timedelta
START_YEAR, END_YEAR = 1901, 1900 + len(g_lunar_month_days)
LUNAR_START_DATE, SOLAR_START_DATE = (1901, 1, 1), datetime(1901,2,19) # 1901年正月初一的公历日期为1901/2/19
LUNAR_END_DATE, SOLAR_END_DATE = (2099, 12, 30), datetime(2100,2,18) # 2099年12月30的公历日期是2100/2/8
def date_diff(tm):
return (tm - SOLAR_START_DATE).days
def get_leap_month(lunar_year):
return (g_lunar_month_days[lunar_year-START_YEAR] >> 16) & 0x0F
def lunar_month_days(lunar_year, lunar_month):
return 29 + ((g_lunar_month_days[lunar_year-START_YEAR] >> lunar_month) & 0x01)
def lunar_year_days(year):
days = 0
months_day = g_lunar_month_days[year - START_YEAR]
for i in range(1, 13 if get_leap_month(year) == 0x0F else 14):
day = 29 + ((months_day>>i)&0x01)
days += day
return days
# 根据公历计算农历日期,返回(year,month,day,isLeap)
def get_lunar_date(tm):
if (tm < SOLAR_START_DATE or tm > SOLAR_END_DATE):
raise Exception('out of range')
span_days = date_diff(tm)
year, month, day = START_YEAR, 1, 1
tmp = lunar_year_days(year)
while span_days >= tmp:
span_days -= tmp
year += 1
tmp = lunar_year_days(year)
leap = False
tmp = lunar_month_days(year, month)
while span_days >= tmp:
span_days -= tmp
month += 1
tmp = lunar_month_days(year, month)
leap_month = get_leap_month(year)
if  month > leap_month:
month -= 1
if month == leap_month:
leap = True
day += span_days
return (year, month, day, leap)
# 根据农历计算公历日期,返回一个数组[datetime1, datetime2],如果为闰月,则可能包含两个日期,否则只有一个
def get_solar_date(year, month, day):
if not(START_YEAR<=year<=END_YEAR and 1<=month<=12 and 1<=day<=30):
raise Exception('out of range')
span_days = 0;
for y in range(START_YEAR, year):
span_days += lunar_year_days(y)
leap_month = get_leap_month(year)
for m in range(1, month + (month > leap_month)):
span_days += lunar_month_days(year, m)
span_days += day - 1
if leap_month == month:
return [SOLAR_START_DATE + timedelta(span_days), SOLAR_START_DATE + timedelta(span_days + lunar_month_days(year, leap_month))]
else:
return [SOLAR_START_DATE + timedelta(span_days)]
if __name__ == '__main__':
import unittest
class Test(unittest.TestCase):
def test_get_lunar_date_out_of_range(self):
self.assertRaises(Exception, get_lunar_date, datetime(1898,12,25))
self.assertRaises(Exception, get_lunar_date, datetime(2100,3,5))

def test_get_lunar_date(self):
self.assertEqual((1987, 6, 1, True), get_lunar_date(datetime(1987, 7, 26)))
self.assertEqual((1987, 6, 1, False), get_lunar_date(datetime(1987, 6, 26)))
self.assertEqual((1950, 5, 12, False), get_lunar_date(datetime(1950, 6, 26)))
self.assertEqual((1985, 2, 29, False),  get_lunar_date(datetime(1985, 4, 18)))
self.assertEqual((2099, 11, 20, False), get_lunar_date(datetime(2099,12,31)))
def test_get_solar_date_out_of_range(self):
self.assertRaises(Exception, get_solar_date, *(1900,3,5))
self.assertRaises(Exception, get_solar_date, *(2100,3,5))
self.assertRaises(Exception, get_solar_date, *(2009,0,5))
self.assertRaises(Exception, get_solar_date, *(2009,13,5))
self.assertRaises(Exception, get_solar_date, *(2009,3,0))
self.assertRaises(Exception, get_solar_date, *(2009,3,31))
def test_get_solar_date(self):
self.assertEqual([datetime(1987,6,26), datetime(1987,7,26)], get_solar_date(1987, 6, 1))
self.assertEqual([datetime(2051,2,10)], get_solar_date(2050, 12, 29))
self.assertEqual([datetime(2009,12,3)], get_solar_date(2009, 10, 17))
self.assertEqual([datetime(2009,5,24), datetime(2009,6,23)], get_solar_date(2009, 5, 1))
#            self.assertEqual([datetime(2009,6,22)], get_solar_date(2009, 5, 30)) # 闰5月没有30号
self.assertEqual([datetime(2009,6,6), datetime(2009,7,6)], get_solar_date(2009, 5, 14))
self.assertEqual([datetime(1920,11,20)], get_solar_date(1920,10,11))
self.assertEqual([datetime(1914,11,21)], get_solar_date(1914,10,5))
self.assertEqual([datetime(1916,2,3)], get_solar_date(1916,1,1))
self.assertEqual([datetime(2099,12,31)],get_solar_date(2099,11,20))
self.assertEqual([datetime(2062,4,21)], get_solar_date(2062,3,12))
unittest.main()

 

运维网声明 1、欢迎大家加入本站运维交流群:群②:261659950 群⑤:202807635 群⑦870801961 群⑧679858003
2、本站所有主题由该帖子作者发表,该帖子作者与运维网享有帖子相关版权
3、所有作品的著作权均归原作者享有,请您和我们一样尊重他人的著作权等合法权益。如果您对作品感到满意,请购买正版
4、禁止制作、复制、发布和传播具有反动、淫秽、色情、暴力、凶杀等内容的信息,一经发现立即删除。若您因此触犯法律,一切后果自负,我们对此不承担任何责任
5、所有资源均系网友上传或者通过网络收集,我们仅提供一个展示、介绍、观摩学习的平台,我们不对其内容的准确性、可靠性、正当性、安全性、合法性等负责,亦不承担任何法律责任
6、所有作品仅供您个人学习、研究或欣赏,不得用于商业或者其他用途,否则,一切后果均由您自己承担,我们对此不承担任何法律责任
7、如涉及侵犯版权等问题,请您及时通知我们,我们将立即采取措施予以解决
8、联系人Email:admin@iyunv.com 网址:www.yunweiku.com

所有资源均系网友上传或者通过网络收集,我们仅提供一个展示、介绍、观摩学习的平台,我们不对其承担任何法律责任,如涉及侵犯版权等问题,请您及时通知我们,我们将立即处理,联系人Email:kefu@iyunv.com,QQ:1061981298 本贴地址:https://www.yunweiku.com/thread-370837-1-1.html 上篇帖子: python的基本方法2 下篇帖子: python爬虫抓站技巧
您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

扫码加入运维网微信交流群X

扫码加入运维网微信交流群

扫描二维码加入运维网微信交流群,最新一手资源尽在官方微信交流群!快快加入我们吧...

扫描微信二维码查看详情

客服E-mail:kefu@iyunv.com 客服QQ:1061981298


QQ群⑦:运维网交流群⑦ QQ群⑧:运维网交流群⑧ k8s群:运维网kubernetes交流群


提醒:禁止发布任何违反国家法律、法规的言论与图片等内容;本站内容均来自个人观点与网络等信息,非本站认同之观点.


本站大部分资源是网友从网上搜集分享而来,其版权均归原作者及其网站所有,我们尊重他人的合法权益,如有内容侵犯您的合法权益,请及时与我们联系进行核实删除!



合作伙伴: 青云cloud

快速回复 返回顶部 返回列表