鱼C论坛

 找回密码
 立即注册
查看: 1574|回复: 0

[技术交流] 万年历

[复制链接]
发表于 2021-8-24 14:43:04 | 显示全部楼层 |阅读模式

马上注册,结交更多好友,享用更多功能^_^

您需要 登录 才可以下载或查看,没有账号?立即注册

x
本帖最后由 wp231957 于 2021-8-24 15:26 编辑


  1. import datetime

  2. class Lunar(object):
  3.     #******************************************************************************
  4.     # 下面为阴历计算所需的数据,为节省存储空间,所以采用下面比较变态的存储方法.
  5.     #******************************************************************************
  6.     #数组g_lunar_month_day存入阴历1901年到2050年每年中的月天数信息,
  7.     #阴历每月只能是29或30天,一年用12(或13)个二进制位表示,对应位为1表30天,否则为29天
  8.     g_lunar_month_day = [
  9.         0x4ae0, 0xa570, 0x5268, 0xd260, 0xd950, 0x6aa8, 0x56a0, 0x9ad0, 0x4ae8, 0x4ae0,   #1910
  10.         0xa4d8, 0xa4d0, 0xd250, 0xd548, 0xb550, 0x56a0, 0x96d0, 0x95b0, 0x49b8, 0x49b0,   #1920
  11.         0xa4b0, 0xb258, 0x6a50, 0x6d40, 0xada8, 0x2b60, 0x9570, 0x4978, 0x4970, 0x64b0,   #1930
  12.         0xd4a0, 0xea50, 0x6d48, 0x5ad0, 0x2b60, 0x9370, 0x92e0, 0xc968, 0xc950, 0xd4a0,   #1940
  13.         0xda50, 0xb550, 0x56a0, 0xaad8, 0x25d0, 0x92d0, 0xc958, 0xa950, 0xb4a8, 0x6ca0,   #1950
  14.         0xb550, 0x55a8, 0x4da0, 0xa5b0, 0x52b8, 0x52b0, 0xa950, 0xe950, 0x6aa0, 0xad50,   #1960
  15.         0xab50, 0x4b60, 0xa570, 0xa570, 0x5260, 0xe930, 0xd950, 0x5aa8, 0x56a0, 0x96d0,   #1970
  16.         0x4ae8, 0x4ad0, 0xa4d0, 0xd268, 0xd250, 0xd528, 0xb540, 0xb6a0, 0x96d0, 0x95b0,   #1980
  17.         0x49b0, 0xa4b8, 0xa4b0, 0xb258, 0x6a50, 0x6d40, 0xada0, 0xab60, 0x9370, 0x4978,   #1990
  18.         0x4970, 0x64b0, 0x6a50, 0xea50, 0x6b28, 0x5ac0, 0xab60, 0x9368, 0x92e0, 0xc960,   #2000
  19.         0xd4a8, 0xd4a0, 0xda50, 0x5aa8, 0x56a0, 0xaad8, 0x25d0, 0x92d0, 0xc958, 0xa950,   #2010
  20.         0xb4a0, 0xb550, 0xb550, 0x55a8, 0x4ba0, 0xa5b0, 0x52b8, 0x52b0, 0xa930, 0x74a8,   #2020
  21.         0x6aa0, 0xad50, 0x4da8, 0x4b60, 0x9570, 0xa4e0, 0xd260, 0xe930, 0xd530, 0x5aa0,   #2030
  22.         0x6b50, 0x96d0, 0x4ae8, 0x4ad0, 0xa4d0, 0xd258, 0xd250, 0xd520, 0xdaa0, 0xb5a0,   #2040
  23.         0x56d0, 0x4ad8, 0x49b0, 0xa4b8, 0xa4b0, 0xaa50, 0xb528, 0x6d20, 0xada0, 0x55b0,   #2050
  24.     ]

  25.     #数组gLanarMonth存放阴历1901年到2050年闰月的月份,如没有则为0,每字节存两年
  26.     g_lunar_month = [
  27.         0x00, 0x50, 0x04, 0x00, 0x20,   #1910
  28.         0x60, 0x05, 0x00, 0x20, 0x70,   #1920
  29.         0x05, 0x00, 0x40, 0x02, 0x06,   #1930
  30.         0x00, 0x50, 0x03, 0x07, 0x00,   #1940
  31.         0x60, 0x04, 0x00, 0x20, 0x70,   #1950
  32.         0x05, 0x00, 0x30, 0x80, 0x06,   #1960
  33.         0x00, 0x40, 0x03, 0x07, 0x00,   #1970
  34.         0x50, 0x04, 0x08, 0x00, 0x60,   #1980
  35.         0x04, 0x0a, 0x00, 0x60, 0x05,   #1990
  36.         0x00, 0x30, 0x80, 0x05, 0x00,   #2000
  37.         0x40, 0x02, 0x07, 0x00, 0x50,   #2010
  38.         0x04, 0x09, 0x00, 0x60, 0x04,   #2020
  39.         0x00, 0x20, 0x60, 0x05, 0x00,   #2030
  40.         0x30, 0xb0, 0x06, 0x00, 0x50,   #2040
  41.         0x02, 0x07, 0x00, 0x50, 0x03    #2050
  42.     ]

  43.     START_YEAR = 1901

  44.     # 天干
  45.     gan = '甲乙丙丁戊己庚辛壬癸'
  46.     # 地支
  47.     zhi = '子丑寅卯辰巳午未申酉戌亥'
  48.     # 生肖
  49.     xiao = '鼠牛虎兔龙蛇马羊猴鸡狗猪'
  50.     # 月份
  51.     lm = '正二三四五六七八九十冬腊'
  52.     # 日份
  53.     ld = '初一初二初三初四初五初六初七初八初九初十十一十二十三十四十五十六十七十八十九二十廿一廿二廿三廿四廿五廿六廿七廿八廿九三十'
  54.     # 节气
  55.     jie = '小寒大寒立春雨水惊蛰春分清明谷雨立夏小满芒种夏至小暑大暑立秋处暑白露秋分寒露霜降立冬小雪大雪冬至'

  56.     def __init__(self, dt = None):
  57.         '''初始化:参数为datetime.datetime类实例,默认当前时间'''
  58.         self.localtime = dt if dt else datetime.datetime.today()

  59.     def sx_year(self): # 返回生肖年
  60.         ct = self.localtime #取当前时间

  61.         year = self.ln_year() - 3 - 1 # 农历年份减3 (说明:补减1)
  62.         year = year % 12 # 模12,得到地支数
  63.         return self.xiao[year]

  64.     def gz_year(self): # 返回干支纪年
  65.         ct = self.localtime #取当前时间
  66.         year = self.ln_year() - 3 - 1 # 农历年份减3 (说明:补减1)
  67.         G = year % 10 # 模10,得到天干数
  68.         Z = year % 12 # 模12,得到地支数
  69.         return self.gan[G] + self.zhi[Z]

  70.     def gz_month(self): # 返回干支纪月(未实现)
  71.         pass

  72.     def gz_day(self): # 返回干支纪日
  73.         ct = self.localtime #取当前时间
  74.         C = ct.year // 100 #取世纪数,减一
  75.         y = ct.year % 100 #取年份后两位(若为1月、2月则当前年份减一)
  76.         y = y - 1 if ct.month == 1 or ct.month == 2 else y
  77.         M = ct.month #取月份(若为1月、2月则分别按13、14来计算)
  78.         M = M + 12 if ct.month == 1 or ct.month == 2 else M
  79.         d = ct.day #取日数
  80.         i = 0 if ct.month % 2 == 1 else 6 #取i (奇数月i=0,偶数月i=6)

  81.         #下面两个是网上的公式
  82.         # http://baike.baidu.com/link?url=MbTKmhrTHTOAz735gi37tEtwd29zqE9GJ92cZQZd0X8uFO5XgmyMKQru6aetzcGadqekzKd3nZHVS99rewya6q
  83.         # 计算干(说明:补减1)
  84.         G = 4 * C + C // 4 + 5 * y + y // 4 + 3 * (M + 1) // 5 + d - 3 - 1
  85.         G = G % 10
  86.         # 计算支(说明:补减1)
  87.         Z = 8 * C + C // 4 + 5 * y + y // 4 + 3 * (M + 1) // 5 + d + 7 + i - 1
  88.         Z = Z % 12

  89.         #返回 干支纪日
  90.         return self.gan[G] + self.zhi[Z]

  91.     def gz_hour(self): # 返回干支纪时(时辰)
  92.         ct = self.localtime #取当前时间
  93.         #计算支
  94.         Z = round((ct.hour/2) + 0.1) % 12 # 之所以加0.1是因为round的bug!!

  95.         #返回 干支纪时(时辰)
  96.         return self.zhi[Z]

  97.     def ln_year(self): # 返回农历年
  98.         year, _, _ = self.ln_date()
  99.         return year

  100.     def ln_month(self): # 返回农历月
  101.         _, month, _ = self.ln_date()
  102.         return month

  103.     def ln_day(self): # 返回农历日
  104.         _, _, day = self.ln_date()
  105.         return day

  106.     def ln_date(self): # 返回农历日期整数元组(年、月、日)(查表法)
  107.         delta_days = self._date_diff()

  108.         #阳历1901年2月19日为阴历1901年正月初一
  109.         #阳历1901年1月1日到2月19日共有49天
  110.         if (delta_days < 49):
  111.             year = self.START_YEAR - 1
  112.             if (delta_days <19):
  113.               month = 11;
  114.               day = 11 + delta_days
  115.             else:
  116.                 month = 12;
  117.                 day = delta_days - 18
  118.             return (year, month, day)

  119.         #下面从阴历1901年正月初一算起
  120.         delta_days -= 49
  121.         year, month, day = self.START_YEAR, 1, 1
  122.         #计算年
  123.         tmp = self._lunar_year_days(year)
  124.         while delta_days >= tmp:
  125.             delta_days -= tmp
  126.             year += 1
  127.             tmp = self._lunar_year_days(year)

  128.         #计算月
  129.         (foo, tmp) = self._lunar_month_days(year, month)
  130.         while delta_days >= tmp:
  131.             delta_days -= tmp
  132.             if (month == self._get_leap_month(year)):
  133.                 (tmp, foo) = self._lunar_month_days(year, month)
  134.                 if (delta_days < tmp):
  135.                     return (0, 0, 0)
  136.                 delta_days -= tmp
  137.             month += 1
  138.             (foo, tmp) = self._lunar_month_days(year, month)

  139.         #计算日
  140.         day += delta_days
  141.         return (year, month, day)

  142.     def ln_date_str(self):# 返回农历日期字符串,形如:农历正月初九
  143.         _, month, day = self.ln_date()
  144.         return '农历{}月{}'.format(self.lm[month-1], self.ld[(day-1)*2:day*2])

  145.     def ln_jie(self): # 返回农历节气
  146.         ct = self.localtime #取当前时间
  147.         year = ct.year
  148.         for i in range(24):
  149.             #因为两个都是浮点数,不能用相等表示
  150.             delta = self._julian_day() - self._julian_day_of_ln_jie(year, i)
  151.             if -.5 <= delta <= .5:
  152.                 return self.jie[i*2:(i+1)*2]
  153.         return ''

  154.     #显示日历
  155.     def calendar(self):
  156.         pass

  157.     #######################################################
  158.     #            下面皆为私有函数
  159.     #######################################################

  160.     def _date_diff(self):
  161.         '''返回基于1901/01/01日差数'''
  162.         return (self.localtime - datetime.datetime(1901, 1, 1)).days

  163.     def _get_leap_month(self, lunar_year):
  164.         flag = self.g_lunar_month[(lunar_year - self.START_YEAR) // 2]
  165.         if (lunar_year - self.START_YEAR) % 2:
  166.             return flag & 0x0f
  167.         else:
  168.             return flag >> 4

  169.     def _lunar_month_days(self, lunar_year, lunar_month):
  170.         if (lunar_year < self.START_YEAR):
  171.             return 30

  172.         high, low = 0, 29
  173.         iBit = 16 - lunar_month;

  174.         if (lunar_month > self._get_leap_month(lunar_year) and self._get_leap_month(lunar_year)):
  175.             iBit -= 1

  176.         if (self.g_lunar_month_day[lunar_year - self.START_YEAR] & (1 << iBit)):
  177.             low += 1

  178.         if (lunar_month == self._get_leap_month(lunar_year)):
  179.             if (self.g_lunar_month_day[lunar_year - self.START_YEAR] & (1 << (iBit -1))):
  180.                  high = 30
  181.             else:
  182.                  high = 29

  183.         return (high, low)

  184.     def _lunar_year_days(self, year):
  185.         days = 0
  186.         for i in range(1, 13):
  187.             (high, low) = self._lunar_month_days(year, i)
  188.             days += high
  189.             days += low
  190.         return days

  191.     # 返回指定公历日期的儒略日(http://blog.csdn.net/orbit/article/details/9210413)
  192.     def _julian_day(self):
  193.         ct = self.localtime #取当前时间
  194.         year = ct.year
  195.         month = ct.month
  196.         day = ct.day

  197.         if month <= 2:
  198.             month += 12
  199.             year -= 1

  200.         B = year / 100
  201.         B = 2 - B + year / 400

  202.         dd = day + 0.5000115740 #本日12:00后才是儒略日的开始(过一秒钟)*/
  203.         return int(365.25 * (year + 4716) + 0.01) + int(30.60001 * (month + 1)) + dd + B - 1524.5

  204.     # 返回指定年份的节气的儒略日数(http://blog.csdn.net/orbit/article/details/9210413)
  205.     def _julian_day_of_ln_jie(self, year, st):
  206.         s_stAccInfo =[
  207.              0.00, 1272494.40, 2548020.60, 3830143.80, 5120226.60, 6420865.80,
  208.              7732018.80, 9055272.60, 10388958.00, 11733065.40, 13084292.40, 14441592.00,
  209.              15800560.80, 17159347.20, 18513766.20, 19862002.20, 21201005.40, 22529659.80,
  210.              23846845.20, 25152606.00, 26447687.40, 27733451.40, 29011921.20, 30285477.60]

  211.         #已知1900年小寒时刻为1月6日02:05:00
  212.         base1900_SlightColdJD = 2415025.5868055555

  213.         if (st < 0) or (st > 24):
  214.             return 0.0

  215.         stJd = 365.24219878 * (year - 1900) + s_stAccInfo[st] / 86400.0

  216.         return base1900_SlightColdJD + stJd




  217. # 测试
  218. def test(ct=None):
  219.     ln = Lunar(ct)
  220.     print('公历 {}  北京时间 {}'.format(ln.localtime.date(), ln.localtime.time()))
  221.     print('{} 【{}】 {}年 {}日 {}时'.format(ln.ln_date_str(), ln.gz_year(), ln.sx_year(), ln.gz_day(), ln.gz_hour()))
  222.     print('节气:{}'.format(ln.ln_jie()))


  223. if __name__ == '__main__':
  224.     ct = datetime.datetime(2015,2,19,13,0,15)
  225.     test(ct)
复制代码
想知道小甲鱼最近在做啥?请访问 -> ilovefishc.com
回复

使用道具 举报

您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

小黑屋|手机版|Archiver|鱼C工作室 ( 粤ICP备18085999号-1 | 粤公网安备 44051102000585号)

GMT+8, 2024-4-28 11:32

Powered by Discuz! X3.4

© 2001-2023 Discuz! Team.

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