摘要:取名讲究五行平衡,生辰八字,五行归属,年月日时四柱如何计算?本文主要实现基于Python的纳音五行测算,解释程序设计思路以及五行测算算法。
一、引言
人们常说生辰八字,是如何测算的?有了生辰八字,如何测试五行归属,网上测试方法五花八门,结果并不统一,通过调研现实师父,调研收集比较一致的生辰和五行测算算法,基于Python开发。
二、天干地支
生辰八字,即对于一个人的出生年月日和时辰,称为四柱,分别为年柱、月柱、日柱和时柱,各自分别对应一个天干和地支,共计八个字,故称八字。
天干地支,简称干支,古代常用于纪年、纪月、纪日和纪时。如甲午中日战争,是指19世纪末日本侵略中国和朝鲜的战争,按中国干支纪年,战争爆发的1894年为甲午年,故称甲午战争。
干支源自中国远古时代对天象的观测。十天干是指“甲、乙、丙、丁、戊、己、庚、辛、壬、癸”,十二地支是指“子、丑、寅、卯、辰、巳、午、未、申、酉、戌、亥”。按照组合,十天干和十二地支依次相配,组成六十个基本单位,两者间按固定顺序相互配合,组成了干支纪元法。干支纪元法六十年一循环,故人们常称六十年为一甲子。本文主要讨论生辰的干支计算以及五行归属。
三、五行
五行,也叫五行学说,是认识世界的基本方式, 即常说的金木水火土,是中国古代哲学家用五行理论来说明世界万物的形成及其相互关系。它强调整体,旨在描述事物的运动形式以及转化关系。阴阳是古代的对立统一学说,五行是原始的系统论。五行有相生相克之说:
相生,是指两类属性不同的事物之间存在相互帮助,相互促进的关系;具体是:木生火,火生土,土生金,金生水,水生木。
相克,则与相生相反,是指两类不同五行属性事物之间关系是相互克制的;具体是:木克土,土克水,水克火、火克金、金克木。
1.正五行
何为正五行,是和纳音五行相对,正五行是指天干和地支对应的五行。
十天干五行:甲乙为木,丙丁为火,戊己为土,庚辛为金,壬癸为水。
十二地支五行:子为水,丑为土,寅卯为木,辰为土,巳午为火,未为土,申酉为金,戌为火,亥为水。
例如生辰八字:‘癸卯’, ‘己未’, ‘丁丑’, '丙午‘,对应的五行为:水木,土土,火土,火火。
2.纳音五行
天干有天干的五行,地支有地支的五行,天干与地支配合后会变成新的五行,称为“纳音五行”,原干支五行称为正五行,纳音五行叫做假借五行,因为它是假借古代五音(宫商角徵羽)和十二音律而组合成的纳音五行。
在实际中使用正五行常与实际有出入,于是便用纳音做了补充,两者之间的关系可以看做是正五行为经,纳音五行为纬。这解释了农历2023年为什么既有人说是水兔年,又有人说为金兔年。网上查询偏水兔的较多,农历2023年的年柱为‘癸卯’,天干对于为水,故称“水兔”(实在不懂为啥这里不管地支);实际在民间,农历2023年的年柱为‘癸卯’,使用纳音五行,对应纳音为“金箔金”,故称“金兔”(参考中华万年历,对每日日柱五行,使用的便是纳音五行)。
纳音五行主要用于推算吉凶祸福、预测运势以及起名等方面(咱不迷信,只为搞清日历计算方法)。
六十甲子纳音表五行歌:
甲子、乙丑——海中金,丙寅、丁卯——炉中火。
戊辰、己巳——大林木,庚午、辛未——路旁土。
壬申、癸酉——剑锋金,甲戌、乙亥——山头火。
丙子、丁丑——涧下水,戊寅、己卯——城头土。
庚辰、辛巳——白蜡金,壬午、癸未——杨柳木。
甲申、乙酉——泉中水,丙戌、丁亥——屋上土。
戊子、己丑——霹雳火,庚寅、辛卯——松柏木。
壬辰、癸巳——长流水,甲午、乙未——沙中金。
丙申、丁酉——山下火,戊戌、己亥——平地木。
庚子、辛丑——壁上土,壬寅、癸卯——金箔金。
甲辰、乙巳——覆灯火,丙午、丁未——天河水。
戊申、己酉——大驿土,庚戌、辛亥——钗钏金。
壬子、癸丑——桑柘木,甲寅、乙卯——大溪水。
丙辰、丁巳——沙中土,戊午、己未——天上火。
庚申、辛酉——石榴木,壬戌、癸亥——大海水。
农历2023年的年柱为‘癸卯’,对照纳音五行,五行为“金箔金”,故称农历2023年为“金兔“”年。
四、程序设计思路
生辰八字的计算算的是农历,故首先需实现公历和农历的转换,计算出了农历之后,再依次实现年柱、月柱、日柱和时柱的计算。
1.公历和农历转换
公历和农历的转换使用第三方库zhdate,使用pip安装:
pip install zhdate
zhdate实现了不用网络接口直接本地计算中国农历,支持农历公历互转,支持计算的农历时间范围为1900年至2100年,实现的基本思路是内置了从1900年至2100年每年的农历春节的公历日期和从1900年到2100年的农历月份数据代码(包括是否闰年、12个月份数的大小情况),作为农历和公历互转的基本知识库,通过计算当前农历日期和当年农历新年之间的天数差值,进一步实现农历和公历的互转,更详细的实现请直接阅读源码。
from datetime import datetime
from zhdate import ZhDate
# 农历转公历
lunar_date = ZhDate(2023, 6, 1) # 新建农历
print(lunar_date) # 查看农历
dt_date = lunar_date .to_datetime() # 农历转换成阳历日期datetime 类型
print(dt_date)
# 公历转农历
dt_date = datetime(2023, 7, 18)
lunar_date = ZhDate.from_datetime(dt_date)
print(lunar_date)
执行结果:
2.年柱算法与实现
年柱计算算法使用农历年,例如2023年1月18日,农历为2022年,应代入2022年进行计算,网上不少算法是直接使用2023年计算的,这回答了为什么要首先实现1.公历和农历转换。
的具体计算公式如下:
g = (year -3) % 10 - 1
d = (year -3) % 12 - 1
年份减3(干支纪年法是从公元4年开始的)除以10取余,余数为天干的字,年份减3除以12取余,余数为地支的字,这样合起来变成了该年的年柱。需注意的是,天干的计算结果为1-10(相应的地支计算结果为1-12),取余后分别为1-9及0,0表示的应该是第10个天干或者第12个地支,在Python中字符串或者列表是从0开始计算的,这样恰造成计算的天干或者地支恰好错一位,故计算出来的1应该表示的是第1个天干或者地支,其索引为0,故减1实现,对于第10个天干或者地支,表示取最后一个天干或者地支,取余结果为0,减1为-1,在Python中有倒序索引,此时取最后一个元素。
年份天干计算实现代码如下:
def __cal_year_ganzhi(self):
# 计算年份的天干地支
gan = TIANGAN[(self.lunar_datetime.year - 3) % 10 - 1]
zhi = DIZHI[(self.lunar_datetime.year - 3) % 12 - 1]
return gan + zhi
3.月柱算法与实现
一年12个月,有12个地支,故每月和地支的关系是固定的,但并不是每月的农历初一为新的地支开始,其和24节气中的节律相关,因此月柱计算算法相比年柱计算较为复杂,网上不少算法计算有误差,并没有按照节律来计算月柱。
(1).地支算法
具体来说按照12节律的顺序:‘立春’, ‘惊蛰’, ‘清明’, ‘立夏’, ‘芒种’, ‘小暑’, ‘立秋’, ‘白露’, ‘寒露’, ‘立冬’, ‘大雪’,‘小寒’,分别对应寅、卯、辰、巳、午、未、申、酉、戌、亥、子、丑;每个节律开始的第一天到下一个节律的前一天,均为对应的地支关系。
故月柱地支的计算问题转换为十二节律的计算问题,十二节律的计算公式为:
int(year % 100 * 0.2422 + values[c] - (year % 100 - 1) // 4)
年数的后2位乘0.2422加该节律对应的C值,并减去闰年数,不同节律的C值及具体映射关系通过字典的形式给出。其中,节律为key,对应月份和C值构成列表为value,列表第一个元素为节律对应的公历月份,其余为该节律对应的C值,C值有两个分别表示对应的20世纪和21世纪的节律C值,一个的表示20世纪和21世纪节律C值相同。
节律日期月份及C值字段如下:
MONTH_C = {
"立春": [2, 3.87],
"惊蛰": [3, 5.63],
"清明": [4, 5.59, 4.81],
"立夏": [5, 6.318, 5.52],
"芒种": [6, 6.5, 5.678],
"小暑": [7, 7.928, 7.108],
"立秋": [8, 8.35, 7.5],
"白露": [9, 8.44, 7.646],
"寒露": [10, 9.098, 8.318],
"立冬": [11, 8.218, 7.438],
"大雪": [12, 7.9, 7.18],
"小寒": [1, 6.11, 5.4055]
}
(2).天干算法
月份天干计算比较简单,使用公历年进行计算:
g = ((year % 5 - 2) * 2 - 1 + 10) % 10 - 1
公历年份除以5的余数减2后乘以2再减1,当该数是负数时加10,最后减1和前面类似,因为Python中的索引从0开始。
(3).代码实现
def __cal_month_ganzhi(self):
# 计算月份的天干地支
year, month, day = self.datetime.year, self.datetime.month, self.datetime.day
cur_date = datetime(year, month, day)
gan_index = ((year % 5 - 2) * 2 - 1 + 10) % 10 - 1
for i in range(len(MONTHS)):
if cur_date < self.month_dates[MONTHS[i]]:
return TIANGAN[(gan_index + i - 2 + 10) % 10] + DIZHI[i]
else:
i += 1
return TIANGAN[(gan_index + i - 2 + 10) % 10] + DIZHI[i%12]
4.日柱算法与实现
日柱的算法暂未找到有效的计算公式,从zhdate公历农历转换处得来的灵感,通过内置的1900年到2100年每年第一天(即元旦日的干支),通过计算具体计算日与元旦的天数差推导出实际的干支,每年元旦日干支字典如下:
YUNDAN_GANZHI = {
'1900': '甲戌', '1901': '己卯', '1902': '甲申', '1903': '己丑', '1904': '甲午', '1905': '庚子', '1906': '乙巳', '1907': '庚戌', '1908': '乙卯', '1909': '辛酉', '1910': '丙寅', '1911': '辛未', '1912': '丙子', '1913': '壬午', '1914': '丁亥', '1915': '壬辰', '1916': '丁酉', '1917': '癸卯', '1918': '戊申', '1919': '癸丑', '1920': '戊午', '1921': '甲子', '1922': '己巳', '1923': '甲戌', '1924': '己卯', '1925': '乙酉', '1926': '庚寅', '1927': '乙未', '1928': '庚子', '1929': '丙午', '1930': '辛亥', '1931': '丙辰', '1932': '辛酉', '1933': '丁卯', '1934': '壬申', '1935': '丁丑', '1936': '壬午', '1937': '戊子', '1938': '癸巳', '1939': '戊戌', '1940': '癸卯', '1941': '己酉', '1942': '甲寅', '1943': '己未', '1944': '甲子', '1945': '庚午', '1946': '乙亥', '1947': '庚辰', '1948': '乙酉', '1949': '辛卯', '1950': '丙申', '1951': '辛丑', '1952': '丙午', '1953': '壬子', '1954': '丁巳', '1955': '壬戌', '1956': '丁卯', '1957': '癸酉', '1958': '戊寅', '1959': '癸未', '1960': '戊子', '1961': '甲午', '1962': '己亥', '1963': '甲辰', '1964': '己酉', '1965': '乙卯', '1966': '庚申', '1967': '乙丑', '1968': '庚午', '1969': '丙子', '1970': '辛巳', '1971': '丙戌', '1972': '辛卯', '1973': '丁酉', '1974': '壬寅', '1975': '丁未', '1976': '壬子', '1977': '戊午', '1978': '癸亥', '1979': '戊辰', '1980': '癸酉', '1981': '己卯', '1982': '甲申', '1983': '己丑', '1984': '甲午', '1985': '庚子', '1986': '乙巳', '1987': '庚戌', '1988': '乙卯', '1989': '辛酉', '1990': '丙寅', '1991': '辛未', '1992': '丙子', '1993': '壬午', '1994': '丁亥', '1995': '壬辰', '1996': '丁酉', '1997': '癸卯', '1998': '戊申', '1999': '癸丑', '2000': '戊午', '2001': '甲子', '2002': '己巳', '2003': '甲戌', '2004': '己卯', '2005': '乙酉', '2006': '庚寅', '2007': '乙未', '2008': '庚子', '2009': '丙午', '2010': '辛亥', '2011': '丙辰', '2012': '辛酉', '2013': '丁卯', '2014': '壬申', '2015': '丁丑', '2016': '壬午', '2017': '戊子', '2018': '癸巳', '2019': '戊戌', '2020': '癸卯', '2021': '己酉', '2022': '甲寅', '2023': '己未', '2024': '甲子', '2025': '庚午', '2026': '乙亥', '2027': '庚辰', '2028': '乙酉', '2029': '辛卯', '2030': '丙申', '2031': '辛丑', '2032': '丙午', '2033': '壬子', '2034': '丁巳', '2035': '壬戌', '2036': '丁卯', '2037': '癸酉', '2038': '戊寅', '2039': '癸未', '2040': '戊子', '2041': '甲午', '2042': '己亥', '2043': '甲辰', '2044': '己酉', '2045': '乙卯', '2046': '庚申', '2047': '乙丑', '2048': '庚午', '2049': '丙子', '2050': '辛巳', '2051': '丙戌', '2052': '辛卯', '2053': '丁酉', '2054': '壬寅', '2055': '丁未', '2056': '壬子', '2057': '戊午', '2058': '癸亥', '2059': '戊辰', '2060': '癸酉', '2061': '己卯', '2062': '甲申', '2063': '己丑', '2064': '甲午', '2065': '庚子', '2066': '乙巳', '2067': '庚戌', '2068': '乙卯', '2069': '辛酉', '2070': '丙寅', '2071': '辛未', '2072': '丙子', '2073': '壬午', '2074': '丁亥', '2075': '壬辰', '2076': '丁酉', '2077': '癸卯', '2078': '戊申', '2079': '癸丑', '2080': '戊午', '2081': '甲子', '2082': '己巳', '2083': '甲戌', '2084': '己卯', '2085': '乙酉', '2086': '庚寅', '2087': '乙未', '2088': '庚子', '2089': '丙午', '2090': '辛亥', '2091': '丙辰', '2092': '辛酉', '2093': '丁卯', '2094': '壬申', '2095': '丁丑', '2096': '壬午', '2097': '戊子', '2098': '癸巳', '2099': '戊戌', '2100': '癸卯'
}
天数差的计算需要注意大小月,以及特殊的2月天数,闰年为29天,平年28天,故还需判断是否闰年,闰年判断实现如下:
def is_leap_year(year):
if year % 4 == 0:
if year % 100 == 0:
if year % 400 == 0:
return True # 能被400整除的是闰年
else:
return False # 能被100整除但不能被400整除的不是闰年
else:
return True # 能被4整除但不能被100整除的是闰年
else:
return False # 不能被4整除的不是闰
大小月的判断通过一个长度为12的列表实现,表示对应月份前面有多少个大月即可:
BIG_MONTHS = [0, 1, 1, 2, 2, 3, 3, 4, 5, 5, 6, 6]
第1个元素表示1月份前有0个大月,第2、3月前面有1个大月,依次类推。
日柱实现代码如下:
def __cal_day_ganzhi(self):
# 计算日期的天干地支
year, month, day = self.datetime.year, self.datetime.month, self.datetime.day
yuan_dan = YUNDAN_GANZHI[str(year)]
days = BIG_MONTHS[month - 1] * 31 + (month - 1 - BIG_MONTHS[month - 1]) * 30 + day
if month > 2:
days -= 1 if self.is_leap_year(year) else 2
gz = list(GANZHI_ELEMENTS.keys())
return gz[(gz.index(yuan_dan) + days - 1) % 60] # 计算的是与元旦的间隔天数,故减1
5.时柱算法与实现
时柱计算比较简单,古人将现在的24小时分为12个时辰,和地支数一致,各时辰和地支的关系是固定的,时辰的换算关系是,小时数加1整除以2的商对12取余,Python索引从0开始,再减1即可。
时柱天干计算公式依赖于日柱天干,为天干数乘以2再加上时支(子丑寅卯辰巳午未申酉戌亥对应时支依次为-1,0,1,。。。,10)得到天干支数。
时柱实现如下:
def __cal_hour_ganzhi(self):
# 计算时间的天干地支
hour = self.datetime.hour
hour = (hour + 1) // 2 % 12
dizhi = DIZHI[hour]
ans = (TIANGAN.index(self.__cal_day_ganzhi()[0]) + 1) * 2 + DIZHI.index(dizhi) - 1
return TIANGAN[ans % 10 - 1] + dizhi
五、程序实现
设计calculate_five_elements类,zhdate为其基类,需要实现农历和公历两种类型时间的五行计算,类参数为农历的年月日,以及小时,基于zhdate基类实现到公历的转换:
class calculate_five_elements(ZhDate):
# 在这里编写根据生辰计算五行的逻辑
def __init__(self, lunar_year, lunar_month, lunar_day, lunar_hour, leap_month=False):
super().__init__(lunar_year, lunar_month, lunar_day, leap_month)
self.lunar_datetime = DATE_TIME(year=lunar_year, month=lunar_month, day=lunar_day, hour=lunar_hour) # 农历时间
self.datetime = super().to_datetime()
self.datetime = DATE_TIME(year=self.datetime.year, month=self.datetime.month, day=self.datetime.day, hour=lunar_hour) # 公历时间
self.month_dates = self.__jieqi_date(datetime(self.datetime.year, self.datetime.month, self.datetime.day))
设计静态函数,实现公历的五行转换,具体为调用zhdate基类转换方法转换公历得到农历,返回calculate_five_elements类,具体实现如下:
@staticmethod
def from_datetime(year, month, day, hour):
cur_date = ZhDate.from_datetime(datetime(year, month, day))
return calculate_five_elements(cur_date.lunar_year, cur_date.lunar_month, cur_date.lunar_day, hour)
最后调用类方法five_elements,返回生辰八字和五行(时柱五行返回了两种结果,一是纳音五行,二是时柱五行只看地支的五行,不同流派计算有差异),方法实现如下:
def five_elements(self):
year_gz = self.__cal_year_ganzhi()
month_gz = self.__cal_month_ganzhi()
day_gz = self.__cal_day_ganzhi()
hour_gz = self.__cal_hour_ganzhi()
gz = [year_gz, month_gz, day_gz, hour_gz]
nayin_five = GANZHI_ELEMENTS[year_gz], GANZHI_ELEMENTS[month_gz], GANZHI_ELEMENTS[day_gz], [GANZHI_ELEMENTS[hour_gz], DIZHI_ELEMENTS[DIZHI.index(hour_gz[-1])]]
return gz, nayin_five
程序使用说明如下:
查询程序参数与使用说明:
python calculate_five_elements.py -h
(1)查询国历日期
查询国历2023年10月20日14点的生辰五行,输入与输出结果如下:
输入:
python calculate_five_elements.py 2023 10 20 14
输出:
(2)查询农历日期
查询农历2023年9月初六 14点的生辰五行,输入与输出结果如下:
输入,其中参数-islun表示当前查询农历日期
python calculate_five_elements.py 2023 9 6 14 -islun
# 或
python calculate_five_elements.py -islun 2023 9 6 14
输出:
(3)测试系统及环境
程序运行系统配置及开发环境如下:
windows10专业版下的WSL2,Centos7.9发行版,python3.10.12
六、程序源代码
(1)Python字节码
完整代码编译的Python字节码文件(.pyc)下载请点击此处。