古代平朔历法基本算法

完整代码: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

 

你可能感兴趣的:(历法,python,天文,历法,平朔,推步)