完整代码:https://github.com/fztransit/AncientCalendar
class Li:
def __init__(self, name, liyuan, yfa, srf, sz, qrf): # 所有历法的共有属性(历名,历元,朔策(yfa/srf),气策(sz/qrf))
self.lm = name
self.ly = liyuan
if yfa < srf: self.yfa = yfa + 29 * srf # 只给出余分
else: self.yfa = yfa
self.srf = srf # 朔日法
self.yue = self.yfa / self.srf
self.sdy, self.sxy = divide(self.yfa, self.srf)
if sz < qrf: self.sz = sz + 365 * qrf
else: self.sz = sz
self.qrf = qrf
self.sui = self.sz / self.qrf
def bjsgz(self, bf):
self.bsgzc = round(self.sui * bf % 60) # 蔀/纪首干支差
bs = 60 // math.gcd(self.bsgzc, 60) # int(self.bsgzc * 60 / math.gcd(self.bsgzc, 60) / self.bsgzc)
jf = bs * bf # 甲子夜半朔旦冬至,四分历为纪法,非四分历即元法
self.bsgz = [0] * bs # 蔀/纪首干支序
for i in range(bs):
self.bsgz[i] = (i * self.bsgzc + self.lyrgz) % 60
return jf
def yrs(self, yxy): # 判断大小月
if yxy < self.srf - self.sxy:
self.yueri = 29
return 29
else:
self.yueri = 30
return 30
def wzqy(self, ydy_0, yxy_0, qdy_0, qxy_0, qxf_0, thisYue=True): # 判断本月是否为无中气月(本月气大小余、朔大小余),无节气月亦适用
if thisYue: ydy, yxy = qy(ydy_0, yxy_0, self.srf, self.sdy, self.sxy)[:2]
else: ydy, yxy = ydy_0, yxy_0 # 输入即次月大小余
qdy, qxy, qxf = qy(qdy_0, qxy_0, self.qrf, self.qdy, self.qxy, qxf_0, self.qxf, self.qfm)
self.yrs(yxy)
if (qdy - ydy) % 60 >= self.yueri: # 本月应有之中气在次月
return '闰', ydy, yxy, qdy_0, qxy_0, qxf_0 # 有闰返回下月朔及本月中气
else:
return '', ydy, yxy, qdy, qxy, qxf # 无闰返回下月朔及下月中气
class Sfl(Li):
type = 1 # 第一类:四分历
yfa = 27759
srf = 940
bf = 76
sz = 1461
qrf = 4
df = 32
zs = 19
zr = 7
jys = 605
qfm = 1
qxf = 0
def __init__(self, name, liyuan, **kwargs):
self.sz *= (self.df / self.qrf)
self.qrf = self.df
Li.__init__(self, name, liyuan, self.yfa, self.srf, self.sz, self.qrf)
self.qdy, self.qxy = divide(self.sz / 12, self.qrf)
try:
self.lyjzss = kwargs['lyjzss']
except:
self.lyjzss = ['冬至', '子', '子']
self.basicData(kwargs)
self.jf = self.bjsgz(self.bf)
self.yf = self.jf * 60 // math.gcd(self.jf, 60) # 元法(岁复甲子)
def basicData(self, kwargs):
# 可能存在的参数,不存在赋默认值
self.zqi = self.sui / 12
self.zq = self.zs * 12 # 章气=章岁*12
self.zy = self.zq + self.zr # 章月=章气+章闰=章岁*12+章闰
self.qly = jieqi.index(self.lyjzss[0])
self.jian = dizhi.index(self.lyjzss[1])
self.suis = dizhi.index(self.lyjzss[2])
if 6 < self.suis < 12: self.suis -= 12
try: self.lyrgz = gz.index(kwargs['lyrgz'])
except: self.lyrgz = 0
try:
self.bsry = kwargs['bsry']
self.lyry = True # 有闰余,历元不正
except:
self.bsry = 0
self.lyry = False
try: self.bm = kwargs['bm']
except: self.bm = ['蔀'] # 无蔀/纪名
def array(self, rank): # 根据不同排表要求修改
# 建表用数据(修改岁首月确定排表用的每年第一个月)
rank = 1
if rank == -1: self.ssy = self.suis if self.suis < 0 else 0 # 冬至和岁首的最小值
elif rank == 0: self.ssy = 0 # 冬至起排
elif rank == 1: self.ssy = self.suis # 从岁首起排
elif rank == 2: self.ssy = self.suis if self.suis > 0 else 0 # 冬至和岁首的最大值
if self.qly % 2 == 1: # 节气为历元,而岁首为中气,需转换
self.bsry = (((self.zqi/2) / self.yue * 228) - self.zr * ((self.qly+1) / 2 - self.ssy)) / 12 # 单位:月
self.backYue = self.ssy - (self.qly + 1) // 2
self.backQi = self.ssy - self.qly / 2
self.jzy = self.jian - self.ssy # 建正所在的月
def dygz(self, dy): # 由蔀首开始求的大余转为干支需加上蔀首干支序
if dy == None: return ' 无 '
gzdy = (dy + self.bsgz[self.rbs]) % 60
return gz[gzdy]
def syjn(self, year): # 算外
self.sy = -self.ly + self.jys * self.yf # 上元
jn = self.sy + year
if year > 0: jn -= 1 # 实际为jn-=1; if year<0,jn+=1
return jn
class Psl(Sfl, Li):
type = 2 # 第二类:使用平朔平气法的历
def __init__(self, name, liyuan, yfa, srf, sz, qrf, zhang, **kwargs):
Li.__init__(self, name, liyuan, yfa, srf, sz, qrf) # 指定父类的构造函数
try:
self.qrf = kwargs['df'] # 度法,纪法约/倍数
self.sz = int(self.sz * self.qrf / qrf)
except: pass
if self.sz / 24 != self.sz // 24:
self.qfm = 24
self.qxf = int(self.sz - self.sz // 24 * 24) * 2
else:
self.qfm, self.qxf = 1, 0
self.qdy, self.qxy = divide((self.sz - self.qxf // 2) / 12, self.qrf)
self.zs, self.zr = zhang
try: self.lyjzss = kwargs['lyjzss']
except: self.lyjzss = ['冬至','寅','寅']
try: self.jf = kwargs['jf'] # 纪法(夜半朔旦冬至)
except: self.jf = qrf
self.bf = qrf
self.basicData(kwargs)
self.yf = self.bjsgz(self.bf) # 元法(甲子夜半朔旦冬至)
try: self.jys = kwargs['jys']
except: self.jys = 0
# 求大小余
def divide(dividend, divisor, flag=False):
quotient = dividend // divisor
remainder = dividend % divisor
if flag: quotient %= 60
return int(quotient), int(remainder)
# 大小余加减运算
def qy(dy1, xy1, rf, dy2, xy2, xf1=0, xf2=0, fm=1, n=1): # 被加/减数,日法,加/减数,被加/减数小分,加/减数小分,分母,加/减次数
zf = ((dy1 * rf + xy1) * fm + xf1) + ((dy2 * rf + xy2) * fm + xf2) * n
xf = zf % fm
zy = (zf - xf) // fm
dy = int(zy // rf)
xy = zy % rf
if fm == 1: return dy % 60, xy + xf, xf
else: return dy % 60, int(xy), xf
# 求天正朔
def tzs(li, year, rank=-1): # rank=-1时历元为冬至即天正气朔,=1时即岁首气朔
li.array(rank)
# 基本推步算法
jn = li.syjn(year)
li.rbn = jn % li.bf # +1入蔀年
if li.type == 1: li.rbs = jn % li.jf // li.bf # +1入蔀数
else:
if li.jf == li.bf: li.rbs = jn % li.yf // li.jf # +1入纪数
else: li.rbs = jn % li.yf // li.bf # +1入蔀数
jy = (li.rbn * li.zy) // li.zs # 蔀内积月,每年235/19月
li.ry = li.rbn * li.zy % li.zs # 闰余 rbn * zy - jy * zf
if li.bsry > 0: # 首月中气非朔
if li.lyry: jy -= li.bsry / li.zs # 历元有闰余,转到无闰余日
if li.ry + li.bsry >= li.zs: jy += 1 # 上一年有闰,需加回
li.ry = (li.bsry + li.zy * li.rbn) % li.zs
ydy, yxy = divide(jy * li.yfa, li.srf, True) # 天正朔蔀内积日 = 积月 * 朔策(yfa / srf)
qdy, qxy = divide(li.rbn * li.sz, li.qrf, True) # 中气积日 = 积年 * 岁长
qxf = 0
if (qdy - ydy) % 60 >= li.yrs(yxy): # 首月为闰月
ydy, yxy = qy(ydy, yxy, li.srf, li.sdy, li.sxy)[:2]
# 根据历元和建正修改至天正冬至或岁首月
if li.backYue != 0 and li.backQi != 0: # 非天正冬至需回推
ydy, yxy, qdy, qxy, qxf = epoch2tz(li, ydy, yxy, qdy, qxy, qxf)
else: li.yrs(yxy)
if li.rbn == 0 and li.ssy < li.qly/2: # 回推时跨到上一蔀(岁首<历元)
ydy = (ydy + li.bsgzc) % 60
qdy = (qdy + li.bsgzc) % 60
return ydy, yxy, qdy, qxy, qxf