-
Notifications
You must be signed in to change notification settings - Fork 2
Expand file tree
/
Copy pathhuangli_query.py
More file actions
190 lines (160 loc) · 6.89 KB
/
Copy pathhuangli_query.py
File metadata and controls
190 lines (160 loc) · 6.89 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
"""Daily 黄历 / 老黄历 query — almanac for a specific solar date.
Usage:
python huangli_query.py [--date YYYY-MM-DD]
Outputs JSON with: 公历, 农历, 干支, 12 建除值神, 28 宿, 宜, 忌,
吉时/凶时, 喜神/财神/福神/贵神方位, 彭祖百忌, 胎神方位, 冲煞.
"""
from __future__ import annotations
import argparse
import sys
from datetime import datetime
from utils import json_print, require_lunar, warn
def _safe(fn, default=None):
try:
return fn()
except Exception:
return default
def _safe_method(obj, name: str, default=None):
"""Call an optional method by name across lunar_python versions."""
try:
fn = getattr(obj, name)
except AttributeError:
return default
return _safe(fn, default)
def _hour_pillars(lunar) -> list[dict]:
"""For each of the 12 传统时辰, return ganzhi + 黄黑道 吉凶 markers.
时辰 boundaries follow the classical odd-start convention (子 23-01,
丑 01-03, 寅 03-05 … 亥 21-23) — NOT even clock blocks (00-02, 02-04 …),
which straddle two 时辰 and mislabel the second half. Each block is
sampled at its midpoint so 干支/天神 are correct for the whole block.
子时 spans this day 23:00 → next day 01:00 (晚子时, sampled at 23:30).
吉凶 comes from the hour's 天神 黄道/黑道 (getTimeTianShenLuck), NOT from
whether the hour has any 宜 — every 时辰 has non-empty 宜, so the latter
would mark all 12 as 吉.
"""
out: list[dict] = []
try:
from lunar_python import Solar # type: ignore
solar = lunar.getSolar()
branches = "子丑寅卯辰巳午未申酉戌亥"
for i, start in enumerate([23, 1, 3, 5, 7, 9, 11, 13, 15, 17, 19, 21]):
s = Solar.fromYmdHms(solar.getYear(), solar.getMonth(),
solar.getDay(), start, 30, 0)
lh = s.getLunar()
out.append({
"shichen": branches[i],
"hour_range": f"{start:02d}:00-{(start + 2) % 24:02d}:00",
"ganzhi": lh.getTimeInGanZhi(),
"tian_shen": _safe_method(lh, "getTimeTianShen", None),
"huang_hei_dao": _safe_method(lh, "getTimeTianShenType", None),
"luck": _safe_method(lh, "getTimeTianShenLuck", None),
"yi": _safe_method(lh, "getTimeYi", []),
"ji": _safe_method(lh, "getTimeJi", []),
"chong_sha": _safe_method(lh, "getTimeChongDesc", None),
})
except Exception as e:
warn(f"hour pillars failed: {e}")
return out
def build_parser() -> argparse.ArgumentParser:
p = argparse.ArgumentParser(
description="黄历日历查询 (今日宜忌 / 神位 / 吉凶时辰)"
)
p.add_argument("--date", type=str, default=None,
help="日期 YYYY-MM-DD (默认今日)")
return p
def main(argv: list[str] | None = None) -> int:
args = build_parser().parse_args(argv)
require_lunar()
from lunar_python import Solar # type: ignore
if args.date:
try:
dt = datetime.strptime(args.date, "%Y-%m-%d")
except ValueError as e:
json_print({"error": "invalid_date_format",
"message": str(e), "expected": "YYYY-MM-DD"})
return 1
else:
dt = datetime.now()
solar = Solar.fromYmdHms(dt.year, dt.month, dt.day, 12, 0, 0)
lunar = solar.getLunar()
# Almanac entries (lunar_python provides rich daily info)
day_yi = _safe_method(lunar, "getDayYi", [])
day_ji = _safe_method(lunar, "getDayJi", [])
zhi_xing = _safe_method(lunar, "getZhiXing", None)
xiu = _safe_method(lunar, "getXiu", None)
zheng = _safe_method(lunar, "getZheng", None)
animal_28 = _safe_method(lunar, "getAnimal", None)
xi_shen = _safe_method(lunar, "getDayPositionXi", None) # 喜神方位
cai_shen = _safe_method(lunar, "getDayPositionCai", None) # 财神方位
fu_shen = _safe_method(lunar, "getDayPositionFu", None) # 福神方位
yang_gui = _safe_method(lunar, "getDayPositionYangGui", None) # 阳贵神
yin_gui = _safe_method(lunar, "getDayPositionYinGui", None) # 阴贵神
tai_shen = _safe_method(lunar, "getDayPositionTai", None)
tai_shen_desc = _safe_method(lunar, "getDayPositionTaiDesc", None)
chong = _safe_method(lunar, "getDayChongDesc", None) or _safe_method(lunar, "getDayChongGan", None)
sha = _safe_method(lunar, "getDaySha", None)
peng_zu_gan = _safe_method(lunar, "getPengZuGan", None)
peng_zu_zhi = _safe_method(lunar, "getPengZuZhi", None)
# 今日吉时/凶时 — 按 时辰黄黑道吉凶 (黄道=吉, 黑道=凶), 非"有无宜事"
ji_xiong_shichen = _hour_pillars(lunar)
ji_shi = [s for s in ji_xiong_shichen if s.get("luck") == "吉"]
xiong_shi = [s for s in ji_xiong_shichen if s.get("luck") == "凶"]
# Nearest jieqi
jieqi_now = _safe(lunar.getJieQi, None)
prev_jq = _safe(lunar.getPrevJieQi, None)
next_jq = _safe(lunar.getNextJieQi, None)
out = {
"input": vars(args),
"solar_date": {
"iso": f"{solar.getYear():04d}-{solar.getMonth():02d}-{solar.getDay():02d}",
"year": solar.getYear(), "month": solar.getMonth(),
"day": solar.getDay(),
},
"lunar_date": {
"year": lunar.getYear(), "month": lunar.getMonth(), "day": lunar.getDay(),
"year_chinese": lunar.getYearInChinese(),
"month_chinese": lunar.getMonthInChinese(),
"day_chinese": lunar.getDayInChinese(),
"zodiac": lunar.getYearShengXiao(),
},
"ganzhi": {
"year": lunar.getYearInGanZhi(),
"month": lunar.getMonthInGanZhi(),
"day": lunar.getDayInGanZhi(),
},
"zhi_shen_12jianchu": zhi_xing,
"xiu_28": {
"xiu": xiu, "zheng": zheng, "animal": animal_28,
"full": (f"{xiu}{zheng}{animal_28}" if xiu else None),
},
"yi": day_yi,
"ji": day_ji,
"ji_shi": ji_shi,
"xiong_shi": xiong_shi,
"shichen_detail": ji_xiong_shichen,
"directions": {
"喜神": xi_shen,
"财神": cai_shen,
"福神": fu_shen,
"阳贵神": yang_gui,
"阴贵神": yin_gui,
},
"peng_zu_bai_ji": {
"gan": peng_zu_gan, "zhi": peng_zu_zhi,
},
"tai_shen_fang_wei": {
"position": tai_shen, "desc": tai_shen_desc,
},
"chong_sha": {
"chong": chong, "sha": sha,
},
"jieqi": {
"today": jieqi_now,
"prev": {"name": prev_jq.getName(), "solar": str(prev_jq.getSolar())} if prev_jq else None,
"next": {"name": next_jq.getName(), "solar": str(next_jq.getSolar())} if next_jq else None,
},
}
json_print(out)
return 0
if __name__ == "__main__":
sys.exit(main())