Files
ZiPingEightChar/scripts/chart/tyme_adapter.py
T
moxiaonian b20fd5c3c9 v3.6.2 同步发版:晚子时不再双重换日、港澳时区归位
- 晚子时(真太阳时 23:00–24:00,约 4% 的盘)修正:换日改由 tyme4py provider 原生实现(cast 新增 sect 参数,1=子初 / 2=子正),排盘层不再自行预滚日。3.6.1 及此前会滚两次,日柱多进一天、时柱随之错干——锚点 1973-08-23 23:32 男:旧 癸巳日壬子时 → 正 壬辰日庚子时。已排过的晚子时盘建议重排核对
- --day-boundary 子正 现真正生效(走 sect=2,晚子时日柱算当天),此前只改审计文案不改实际排盘
- data/cities.json 港澳 28 个条目由 Asia/Shanghai 改为 Asia/Hong_Kong(19)、Asia/Macau(9):大陆 1986–1991 行夏令时而港澳 1979 年后无,沿用上海区会把大陆夏令时错加到港澳命主身上
- 修副本环境 `pytest tests/` 收集期中断(test_sync_tool 模块级加载外层 tools/,skipif 挡不住导入),现按设计整模块 skip
- .gitignore 新增 data/.license.json、data/.update_check.json,本机授权与更新缓存不再可能误入仓
- 测试 178 项全绿(副本内 sync 工具 4 项按设计 skip);manifest 1278 文件,ed25519 签名,公钥指纹 3089bc1cc9a453e5…
2026-10-12 19:05:09 +08:00

107 lines
4.3 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.
# -*- coding: utf-8 -*-
"""tyme4py 唯一接触面:输入真太阳时时刻,返回四柱及派生信息(纯计算,无命理判断)。"""
from datetime import datetime
import tyme4py
from tyme4py.solar import SolarTime, SolarTerm
LIBRARY_VERSION = tyme4py.__version__
_PILLARS = ("year", "month", "day", "hour")
def _solar_time_to_dt(t: SolarTime) -> datetime:
return datetime(t.get_year(), t.get_month(), t.get_day(),
t.get_hour(), t.get_minute(), t.get_second())
def _jie_bounds(cast_dt: datetime) -> tuple:
"""出生时刻前后最近的两个节令(is_jie()),以出生年为中心 ±1 年遍历。"""
prev_jie = next_jie = None
for y in (cast_dt.year - 1, cast_dt.year, cast_dt.year + 1):
for i in range(24):
term = SolarTerm.from_index(y, i)
if not term.is_jie():
continue
t = _solar_time_to_dt(term.get_julian_day().get_solar_time())
if t < cast_dt and (prev_jie is None or t > prev_jie[1]):
prev_jie = (term, t)
elif t > cast_dt and (next_jie is None or t < next_jie[1]):
next_jie = (term, t)
return prev_jie, next_jie
def _hidden(me, earth_branch) -> list:
items = []
for getter in ("get_hide_heaven_stem_main",
"get_hide_heaven_stem_middle",
"get_hide_heaven_stem_residual"):
hs = getattr(earth_branch, getter)()
if hs is not None:
items.append([hs.get_name(), me.get_ten_star(hs).get_name()])
return items
def _shen_sha(ganzhi: dict, gender: str) -> dict:
"""子平通行神煞(查法见 chart/shensha.py 与 rules/94_神煞细节.md)。"""
from chart.shensha import evaluate_natal, STAR_ORDER
raw = evaluate_natal(ganzhi, gender)
order = {name: i for i, name in enumerate(STAR_ORDER)}
return {
key: [f"{star}({note})"
for star, note in sorted(entries, key=lambda e: order[e[0]])]
for key, entries in raw.items()
}
def cast(cast_dt: datetime, gender: str, sect: int = 1) -> dict:
"""cast_dt:真太阳时(naive,北京时间语义)。
sect:日界流派。1=子初换日(tyme4py 默认 provider,晚子时 23:xx 日柱归次日);
2=子正换日(LunarSect2,晚子时日柱算当天)。晚子时的换日由 provider
原生实现,调用方不得再对 cast_dt 预先滚日(否则双重换日)。
"""
from tyme4py.eightchar.provider.impl import (
DefaultEightCharProvider, LunarSect2EightCharProvider,
)
from tyme4py.lunar import LunarHour
prev_provider = LunarHour.provider
LunarHour.provider = (LunarSect2EightCharProvider() if sect == 2
else DefaultEightCharProvider())
try:
solar = SolarTime.from_ymd_hms(cast_dt.year, cast_dt.month, cast_dt.day,
cast_dt.hour, cast_dt.minute, cast_dt.second)
ec = solar.get_lunar_hour().get_eight_char()
finally:
LunarHour.provider = prev_provider
pillars = {k: getattr(ec, f"get_{k}")() for k in _PILLARS}
me = ec.get_day().get_heaven_stem()
prev_jie, next_jie = _jie_bounds(cast_dt)
base = {k: {"gan": p.get_heaven_stem().get_name(),
"zhi": p.get_earth_branch().get_name()} for k, p in pillars.items()}
ganzhi = {k: v["gan"] + v["zhi"] for k, v in base.items()}
return {
**base,
"day_master": me.get_name(),
"ten_god": {k: me.get_ten_star(pillars[k].get_heaven_stem()).get_name()
for k in ("year", "month", "hour")},
"hidden": {k: _hidden(me, pillars[k].get_earth_branch()) for k in _PILLARS},
"terrain": {k: me.get_terrain(pillars[k].get_earth_branch()).get_name()
for k in _PILLARS},
"na_yin": {k: pillars[k].get_sound().get_name() for k in _PILLARS},
"void": {k: "".join(b.get_name() for b in pillars[k].get_extra_earth_branches())
for k in ("day", "hour")},
"shen_sha": _shen_sha(ganzhi, gender),
"prev_jie": {"name": prev_jie[0].get_name(), "time": prev_jie[1]},
"next_jie": {"name": next_jie[0].get_name(), "time": next_jie[1]},
"tai_yuan": ec.get_fetal_origin().get_name(),
"ming_gong": ec.get_own_sign().get_name(),
"library_version": LIBRARY_VERSION,
"raw": ec,
}