99 lines
4.4 KiB
Python
99 lines
4.4 KiB
Python
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
|