# -*- coding:utf-8 -*- from __future__ import annotations import warnings from math import ceil from typing import TYPE_CHECKING, Union, List from tyme4py import AbstractCulture, AbstractTyme from tyme4py.culture import Duty from tyme4py.eightchar.provider import ChildLimitProvider from tyme4py.eightchar.provider.impl import DefaultChildLimitProvider from tyme4py.enums import Gender, YinYang if TYPE_CHECKING: from tyme4py.lunar import LunarYear from tyme4py.solar import SolarTime, SolarTerm, SolarDay from tyme4py.sixtycycle import SixtyCycle, SixtyCycleYear, ThreePillars class EightChar(AbstractCulture): """八字""" def __init__(self, year: Union[SixtyCycle, str], month: Union[SixtyCycle, str], day: Union[SixtyCycle, str], hour: Union[SixtyCycle, str]): """ 初始化 :param year: 年干支 :param month: 月干支 :param day: 日干支 :param hour: 时干支 """ from tyme4py.sixtycycle import SixtyCycle, ThreePillars self._three_pillars = ThreePillars(year if isinstance(year, SixtyCycle) else SixtyCycle(year), month if isinstance(month, SixtyCycle) else SixtyCycle(month), day if isinstance(day, SixtyCycle) else SixtyCycle(day)) self._hour = hour if isinstance(hour, SixtyCycle) else SixtyCycle(hour) def get_year(self) -> SixtyCycle: """ 年柱 :return: 干支 SixtyCycle """ return self._three_pillars.get_year() def get_month(self) -> SixtyCycle: """ 月柱 :return: 干支 SixtyCycle """ return self._three_pillars.get_month() def get_day(self) -> SixtyCycle: """ 日柱 :return: 干支 SixtyCycle """ return self._three_pillars.get_day() def get_hour(self) -> SixtyCycle: """ 时柱 :return: 干支 SixtyCycle """ return self._hour def get_fetal_origin(self) -> SixtyCycle: """ 胎元 :return: 干支 SixtyCycle """ from tyme4py.sixtycycle import SixtyCycle m: SixtyCycle = self.get_month() return SixtyCycle.from_index(m.get_heaven_stem().next(1).get_index() * 6 - m.get_earth_branch().next(3).get_index() * 5) def get_fetal_breath(self) -> SixtyCycle: """ 胎息 :return: 干支 SixtyCycle """ from tyme4py.sixtycycle import SixtyCycle d: SixtyCycle = self.get_day() return SixtyCycle.from_index(d.get_heaven_stem().next(5).get_index() * 6 + d.get_earth_branch().get_index() * 5 - 65) def get_own_sign(self) -> SixtyCycle: """ 命宫 :return: 干支 SixtyCycle """ from tyme4py.sixtycycle import SixtyCycle return SixtyCycle.from_index(self.get_year().get_heaven_stem().get_index() * 12 + (27 - self.get_month().get_earth_branch().get_index() - self._hour.get_earth_branch().get_index()) % 12 + 2) def get_body_sign(self) -> SixtyCycle: """ 身宫 :return: 干支 SixtyCycle """ from tyme4py.sixtycycle import SixtyCycle return SixtyCycle.from_index(self.get_year().get_heaven_stem().get_index() * 12 + (11 + self.get_month().get_earth_branch().get_index() + self._hour.get_earth_branch().get_index()) % 12 + 2) def get_duty(self) -> Duty: """ 建除十二值神 :return: 建除十二值神 """ warnings.warn('get_duty() is deprecated, please use SixtyCycleDay.get_duty() instead.', DeprecationWarning) return Duty(self.get_day().get_earth_branch().get_index() - self.get_month().get_earth_branch().get_index()) def get_name(self) -> str: return f'{self._three_pillars} {self._hour}' def get_solar_times(self, start_year: int, end_year: int) -> List[SolarTime]: """ 八字转公历时刻列表 :param start_year: 开始年份,支持1-9999年 :param end_year: 结束年份,支持1-9999年 :return: 公历时刻 SolarTime的列表 """ from tyme4py.sixtycycle import HeavenStem from tyme4py.solar import SolarTime, SolarTerm l: List[SolarTime] = [] year = self.get_year() month = self.get_month() day = self.get_day() # 月地支距寅月的偏移值 m: int = month.get_earth_branch().next(-2).get_index() # 月天干要一致 if not HeavenStem((year.get_heaven_stem().get_index() + 1) * 2 + m) == month.get_heaven_stem(): return l # 1年的立春是辛酉,序号57 y: int = year.next(-57).get_index() + 1 # 节令偏移值 m *= 2 # 时辰地支转时刻 h: int = self._hour.get_earth_branch().get_index() * 2 # 兼容子时多流派 hours: List[int] = [0, 23] if h == 0 else [h] base_year: int = start_year - 1 if base_year > y: y += 60 * int(ceil((base_year - y) / 60.0)) while y <= end_year: # 立春为寅月的开始 term: SolarTerm = SolarTerm(y, 3) # 节令推移,年干支和月干支就都匹配上了 if m > 0: term = term.next(m) solar_time: SolarTime = term.get_julian_day().get_solar_time() if solar_time.get_year() >= start_year: # 日干支和节令干支的偏移值 solar_day: SolarDay = solar_time.get_solar_day() d: int = day.next(-solar_day.get_lunar_day().get_sixty_cycle().get_index()).get_index() if d > 0: # 从节令推移天数 solar_day = solar_day.next(d) for i in range(0, hours.__len__()): mi: int = 0 s: int = 0 hour: int = hours[i] if d == 0 and hour == solar_time.get_hour(): # 如果正好是节令当天,且小时和节令的小时数相等的极端情况,把分钟和秒钟带上 mi = solar_time.get_minute() s = solar_time.get_second() time: SolarTime = SolarTime(solar_day.get_year(), solar_day.get_month(), solar_day.get_day(), hour, mi, s) if d == 30: time = time.next(-3600) # 验证一下 if time.get_lunar_hour().get_eight_char() == self: l.append(time) y += 60 return l class ChildLimitInfo: """童限信息""" def __init__(self, start_time: SolarTime, end_time: SolarTime, year_count: int, month_count: int, day_count: int, hour_count: int, minute_count: int): """ 初始化 :param start_time: 开始(即出生)的公历时刻 :param end_time: 结束(即开始起运)的公历时刻 :param year_count: 年数 :param month_count: 月数 :param day_count: 日数 :param hour_count: 小时数 :param minute_count: 分钟数 """ self._start_time = start_time self._end_time = end_time self._year_count = year_count self._month_count = month_count self._day_count = day_count self._hour_count = hour_count self._minute_count = minute_count def get_start_time(self) -> SolarTime: """ :return: 开始(即出生)的公历时刻 """ return self._start_time def get_end_time(self) -> SolarTime: """ :return: 结束(即开始起运)的公历时刻 """ return self._end_time def get_year_count(self) -> int: """ :return: 年数 """ return self._year_count def get_month_count(self) -> int: """ :return: 月数 """ return self._month_count def get_day_count(self) -> int: """ :return: 日数 """ return self._day_count def get_hour_count(self) -> int: """ :return: 小时数 """ return self._hour_count def get_minute_count(self) -> int: """ :return: 分钟数 """ return self._minute_count class ChildLimit: """童限(从出生到起运的时间段)""" provider: ChildLimitProvider = DefaultChildLimitProvider() """童限计算接口""" def __init__(self, birth_time: SolarTime, gender: Gender): """ 通过出生公历时刻初始化 :param birth_time: 出生公历时刻 :param gender: 性别 """ self._gender = gender self._eight_char = birth_time.get_lunar_hour().get_eight_char() # 阳男阴女顺推,阴男阳女逆推 yang: bool = YinYang.YANG == self._eight_char.get_year().get_heaven_stem().get_yin_yang() man: bool = Gender.MAN == gender self._forward = (yang and man) or (not yang and not man) term: SolarTerm = birth_time.get_term() if not term.is_jie(): term = term.next(-1) if self._forward: term = term.next(2) self._info = ChildLimit.provider.get_info(birth_time, term) def get_eight_char(self) -> EightChar: """ :return: 八字 """ return self._eight_char def get_gender(self) -> Gender: """ :return: 性别 """ return self._gender def get_year_count(self) -> int: """ :return: 年数 """ return self._info.get_year_count() def get_month_count(self) -> int: """ :return: 月数 """ return self._info.get_month_count() def get_day_count(self) -> int: """ :return: 日数 """ return self._info.get_day_count() def get_hour_count(self) -> int: """ :return: 小时数 """ return self._info.get_hour_count() def get_minute_count(self) -> int: """ :return: 分钟数 """ return self._info.get_minute_count() def get_start_time(self) -> SolarTime: """ 开始(即出生)的公历时刻 :return: 公历时刻 """ return self._info.get_start_time() def get_end_time(self) -> SolarTime: """ 结束(即开始起运)的公历时刻 :return: 公历时刻 """ return self._info.get_end_time() def is_forward(self) -> bool: """ 是否顺推 :return: true/false """ return self._forward def get_start_decade_fortune(self) -> DecadeFortune: """ :return: 起运大运 """ return DecadeFortune(self, 0) def get_decade_fortune(self) -> DecadeFortune: """ :return: 所属大运 """ return DecadeFortune(self, -1) def get_start_fortune(self) -> Fortune: """ :return: 小运 """ return Fortune(self, 0) def get_end_lunar_year(self) -> LunarYear: """ 结束农历年 :return: 农历年 """ warnings.warn('get_end_lunar_year() is deprecated, please use SixtyCycleDay.get_duty() instead.', DeprecationWarning) from tyme4py.lunar import LunarYear return LunarYear.from_year(self.get_start_time().get_lunar_hour().get_year() + self.get_end_time().get_year() - self.get_start_time().get_year()) def get_start_sixty_cycle_year(self) -> SixtyCycleYear: """ 开始(即出生)干支年 :return: 干支年 """ from tyme4py.sixtycycle import SixtyCycleYear return SixtyCycleYear.from_year(self.get_start_time().get_year()) def get_end_sixty_cycle_year(self) -> SixtyCycleYear: """ 结束(即起运)干支年 :return: 干支年 """ from tyme4py.sixtycycle import SixtyCycleYear return SixtyCycleYear.from_year(self.get_end_time().get_year()) @staticmethod def get_start_age() -> int: """ 开始年龄 :return: 数字 """ return 1 def get_end_age(self) -> int: """ 结束年龄 :return: 数字 """ n: int = self.get_end_sixty_cycle_year().get_year() - self.get_start_sixty_cycle_year().get_year() return max(n, 1) class Fortune(AbstractTyme): """小运--在十年大运中,每一年为一小运。童限结束的公历时刻,既是大运的开始,也是小运的开始。""" def __init__(self, child_limit: ChildLimit, index: int): """ 通过童限初始化 :param child_limit: 童限 :param index: 序号 """ self._child_limit = child_limit self._index = index def get_age(self) -> int: """ :return: 年龄 """ return self._child_limit.get_end_sixty_cycle_year().get_year() - self._child_limit.get_start_sixty_cycle_year().get_year() + 1 + self._index def get_lunar_year(self) -> LunarYear: """ :return: 农历年 """ warnings.warn('get_lunar_year() is deprecated, please use get_sixty_cycle_year() instead.', DeprecationWarning) return self._child_limit.get_end_lunar_year().next(self._index) def get_sixty_cycle_year(self) -> SixtyCycleYear: """ :return: 干支年 """ return self._child_limit.get_end_sixty_cycle_year().next(self._index) def get_sixty_cycle(self) -> SixtyCycle: """ :return: 干支 """ n: int = self.get_age() return self._child_limit.get_eight_char().get_hour().next(n if self._child_limit.is_forward() else -n) def get_name(self) -> str: return self.get_sixty_cycle().get_name() def next(self, n: int) -> Fortune: return Fortune(self._child_limit, self._index + n) class DecadeFortune(AbstractTyme): """大运(10年1大运)--自起运开始,每十年为一大运。童限结束的公历时刻,即开始起运,是大运的开始。""" def __init__(self, child_limit: ChildLimit, index: int): """ 通过童限初始化 :param child_limit: 童限 :param index: 序号 """ self._child_limit = child_limit self._index = index def get_start_age(self) -> int: """ :return: 开始年龄 """ return self._child_limit.get_end_sixty_cycle_year().get_year() - self._child_limit.get_start_sixty_cycle_year().get_year() + 1 + self._index * 10 def get_end_age(self) -> int: """ :return: 结束年龄 """ return self.get_start_age() + 9 def get_start_lunar_year(self) -> LunarYear: """ 开始农历年 :return: 农历年 """ warnings.warn('get_lunar_year() is deprecated, please use get_sixty_cycle_year() instead.', DeprecationWarning) return self._child_limit.get_end_lunar_year().next(self._index * 10) def get_end_lunar_year(self) -> LunarYear: """ 结束农历年 :return: 农历年 """ warnings.warn('get_lunar_year() is deprecated, please use get_sixty_cycle_year() instead.', DeprecationWarning) return self.get_start_lunar_year().next(9) def get_start_sixty_cycle_year(self) -> SixtyCycleYear: """ 开始干支年 :return: 干支年 """ return self._child_limit.get_end_sixty_cycle_year().next(self._index * 10) def get_end_sixty_cycle_year(self) -> SixtyCycleYear: """ 结束干支年 :return: 干支年 """ return self.get_start_sixty_cycle_year().next(9) def get_sixty_cycle(self) -> SixtyCycle: """ :return: 干支 """ return self._child_limit.get_eight_char().get_month().next(self._index + 1 if self._child_limit.is_forward() else -self._index - 1) def get_name(self) -> str: return self.get_sixty_cycle().get_name() def next(self, n: int) -> DecadeFortune: return DecadeFortune(self._child_limit, self._index + n) def get_start_fortune(self) -> Fortune: """ 本轮大运中开始的小运 :return: 小运 Fortune """ return Fortune(self._child_limit, self._index * 10)