people-reading/backend/app/services/bazi_calculator.py
2026-05-12 17:05:32 +08:00

99 lines
4.4 KiB
Python
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

from datetime import datetime
try:
from lunar_python import Lunar, Solar
except ImportError: # pragma: no cover - production image installs the package
Lunar = None
Solar = None
class BaziCalculator:
def calculate(self, payload: dict) -> dict:
birth_date = payload["birth_date"]
time_unknown = bool(payload.get("time_unknown"))
birth_time = payload.get("birth_time") if not time_unknown else "12:00"
hour, minute = self._parse_time(birth_time or "12:00")
year, month, day = self._parse_date(birth_date)
calendar_type = payload.get("calendar_type", "solar")
if Solar and Lunar:
if calendar_type == "lunar":
lunar_month = -month if payload.get("is_leap_month") else month
lunar = Lunar.fromYmdHms(year, lunar_month, day, hour, minute, 0)
solar = lunar.getSolar()
else:
solar = Solar.fromYmdHms(year, month, day, hour, minute, 0)
lunar = solar.getLunar()
eight = lunar.getEightChar()
return {
"calendar_type": calendar_type,
"is_leap_month": bool(payload.get("is_leap_month")),
"birth_date": birth_date,
"birth_time": None if time_unknown else f"{hour:02d}:{minute:02d}",
"time_unknown": time_unknown,
"birth_place": payload.get("birth_place"),
"nickname": payload.get("nickname"),
"gender": payload.get("gender"),
"solar_date": f"{solar.getYear():04d}-{solar.getMonth():02d}-{solar.getDay():02d}",
"lunar_date": f"{lunar.getYear()}{lunar.getMonth()}{lunar.getDay()}",
"pillars": {
"year": eight.getYear(),
"month": eight.getMonth(),
"day": eight.getDay(),
"time": eight.getTime() if not time_unknown else "时辰不详",
},
"wuxing": {
"year": eight.getYearWuXing(),
"month": eight.getMonthWuXing(),
"day": eight.getDayWuXing(),
"time": eight.getTimeWuXing() if not time_unknown else "时辰不详",
},
"shi_shen": {
"year": eight.getYearShiShenGan(),
"month": eight.getMonthShiShenGan(),
"day": "日主",
"time": eight.getTimeShiShenGan() if not time_unknown else "时辰不详",
},
"day_master": eight.getDayGan(),
}
return self._fallback_chart(payload, year, month, day, hour, minute, time_unknown)
def _fallback_chart(self, payload: dict, year: int, month: int, day: int, hour: int, minute: int, time_unknown: bool) -> dict:
stems = "甲乙丙丁戊己庚辛壬癸"
branches = "子丑寅卯辰巳午未申酉戌亥"
seed = year * 372 + month * 31 + day + hour
def pillar(offset: int) -> str:
return stems[(seed + offset) % 10] + branches[(seed + offset) % 12]
return {
"calendar_type": payload.get("calendar_type", "solar"),
"is_leap_month": bool(payload.get("is_leap_month")),
"birth_date": payload["birth_date"],
"birth_time": None if time_unknown else f"{hour:02d}:{minute:02d}",
"time_unknown": time_unknown,
"birth_place": payload.get("birth_place"),
"nickname": payload.get("nickname"),
"gender": payload.get("gender"),
"solar_date": payload["birth_date"],
"lunar_date": "本地未安装 lunar_python暂用娱乐排盘",
"pillars": {
"year": pillar(0),
"month": pillar(13),
"day": pillar(27),
"time": pillar(41) if not time_unknown else "时辰不详",
},
"wuxing": {"year": "参考", "month": "参考", "day": "参考", "time": "参考"},
"shi_shen": {"year": "参考", "month": "参考", "day": "日主", "time": "参考"},
"day_master": pillar(27)[0],
}
def _parse_date(self, value: str) -> tuple[int, int, int]:
parsed = datetime.strptime(value, "%Y-%m-%d")
return parsed.year, parsed.month, parsed.day
def _parse_time(self, value: str) -> tuple[int, int]:
parsed = datetime.strptime(value, "%H:%M")
return parsed.hour, parsed.minute