# 文档 (/zh/docs) 把出生时间算成一张完整的紫微斗数命盘,并一键转成大模型读得懂的文字。Rust 核心,供 Rust、Python、Go 调用。 把出生时间算成一张完整的紫微斗数命盘,并能一键转成大模型读得懂的文字 —— **排盘交给它算准,解读交给 AI**。 一行调用得到的就是下面这段文字,直接贴进任何大模型就能开始问: ```text === 基本信息 === 性别: 女 阳历: 2000-8-16 农历: 二〇〇〇年七月十七 干支: 庚辰 甲申 丙午 庚寅 时辰: 寅时 (03:00~05:00) 星座: 狮子座 生肖: 龙 命宫地支: 午 身宫地支: 戌 命主: 破军 身主: 文昌 五行局: 木三局 生年四化: 太阳禄, 武曲权, 太阴科, 天同忌 === 十二宫 === --- 财帛 --- 天干地支: 戊寅 大限: 43-52 小限虚岁: 9, 21, 33, 45, 57, 69, 81, 93, 105, 117 十二神: 绝, 飞廉, 吊客, 岁驿 主星: 武曲(得)[权], 天相(庙) 辅星: 天马 杂耀: 解神, 三台, 天寿, 天巫, 天厨, 阴煞, 天哭 (其余十一宫略) ``` 排出来的盘准不准,有一条硬标准:**与 JS [iztro](https://github.com/SylarLong/iztro) v2.5.8 逐字段零差异**, 由约 71 万例金标测试守着。见[准确性保证](/zh/docs/guide/about/accuracy)。 ## 从哪开始 [#从哪开始] ## 三种编程语言,同一套结果 [#三种编程语言同一套结果] 三套绑定调用同一份 Rust 核心,因此排盘结果逐字段相同。 判断方法基于语言无关标识,同一条分析规则在三种编程语言上写出来、结果也一致。 ```rust use x_iztro::*; let chart = by_solar("2000-8-16", 2, Gender::Female, true, Language::ZhCN, Config::default())?; let soul = chart.palace(Palace::Soul).unwrap(); println!("{}", soul.has(&[StarKey::ZiweiMaj])); ``` ```python from x_iztro import Astro chart = Astro().by_solar("2000-8-16", 2, "female") soul = chart.palace("soulPalace") print(soul.has(["ziweiMaj"])) ``` ```go chart, _ := iztro.BySolar("2000-8-16", 2, iztro.GenderFemale, true, iztro.LanguageZhCN, nil) soul := chart.Palace(iztro.PalaceSoul) fmt.Println(soul.Has(iztro.StarZiweiMaj)) ``` 三段代码的输出都是 `true`:这张盘的命宫里有紫微星。 # 介绍 (/zh/docs/guide) 把出生时间算成一张完整的紫微斗数命盘,并一键转成大模型读得懂的文字。Rust 核心,供 Rust、Python、Go 调用,结果与 JS iztro 逐字段一致。 *适合:开发者 · 命理爱好者 · 产品与决策者* 把出生时间算成一张完整的紫微斗数命盘,并能一键转成大模型读得懂的文字 —— **排盘交给它算准,解读交给 AI**。 一行调用得到的就是这段文字,直接贴进任何大模型就能开始问: ```text === 基本信息 === 性别: 女 阳历: 2000-8-16 农历: 二〇〇〇年七月十七 干支: 庚辰 甲申 丙午 庚寅 时辰: 寅时 (03:00~05:00) 星座: 狮子座 生肖: 龙 命宫地支: 午 身宫地支: 戌 命主: 破军 身主: 文昌 五行局: 木三局 生年四化: 太阳禄, 武曲权, 太阴科, 天同忌 === 十二宫 === --- 财帛 --- 天干地支: 戊寅 大限: 43-52 小限虚岁: 9, 21, 33, 45, 57, 69, 81, 93, 105, 117 十二神: 绝, 飞廉, 吊客, 岁驿 主星: 武曲(得)[权], 天相(庙) 辅星: 天马 杂耀: 解神, 三台, 天寿, 天巫, 天厨, 阴煞, 天哭 (其余十一宫略) ``` 排出来的盘准不准,有一条硬标准:**与 JS [iztro](https://github.com/SylarLong/iztro) v2.5.8 逐字段零差异**, 由约 71 万例金标测试守着。 核心用 Rust 实现,通过三套绑定暴露给上层编程语言: ## 它解决什么问题 [#它解决什么问题] 紫微斗数排盘看似只是查表,实际上牵扯一连串容易出错的历法与流派细节:农历闰月的处理、 晚子时算今天还是明天、年干支按正月初一还是立春换年、虚岁怎么进位、不同流派的四化表差异。 任何一处取舍不同,排出的盘就不是同一张。 社区里最完整的开源实现是 JavaScript 的 [iztro](https://github.com/SylarLong/iztro), 但它只能在 JS 运行时里用。x-iztro 把这套逻辑完整移植到 Rust, 让服务端、数据分析脚本、命令行工具、移动端也能用上同一套排盘结果。 x-iztro 是 iztro v2.5.8 的移植,不是重新发明。凡是 iztro 有的功能与数据, 两者结果必须逐字段一致 —— 这条由约 71 万例金标测试守着, 详见[准确性保证](/zh/docs/guide/about/accuracy)。 ## 特性 [#特性] ### 一键转成 AI 能读的文字 [#一键转成-ai-能读的文字] 内置 Prompt 生成:把一张盘或一段运限转成上面那种结构化文本,直接交给大模型分析, 不必自己拼接命盘描述。见 [AI Prompt 生成](/zh/docs/guide/guides/ai-prompt)。 ### 排盘与运限完整 [#排盘与运限完整] 本命盘、大限、小限、童限、流年、流月、流日、流时,六个层级的宫位、星耀、四化与 三方四正全部可取。年系杂耀、岁前十二神、将前十二神、博士十二神、长生十二神一应俱全。 ### 流派与分界点可配置 [#流派与分界点可配置] `Config` 的六个开关覆盖了实践中会分歧的每一处:年分界点、运限分界点、虚岁分界点、 晚子时归属、算法派别(默认 / 中州派)、排盘视角(天盘 / 地盘 / 人盘), 另可整表替换四化表与亮度表。默认值与 JS iztro 一致, 细节见 [Config 详解](/zh/docs/guide/guides/config)。 ### 六种盘面语言 [#六种盘面语言] 简体中文、繁体中文、英文、日文、韩文、越南文。同一张盘换盘面语言只是换一次翻译, 排盘结果不受影响。 ### 中文盘、英文盘,判断结果一样 [#中文盘英文盘判断结果一样] 给开发者 星名宫名在不同盘面语言下文本不同,但每个实体都额外带一个稳定的标识(key)。 Python 的枚举与 Go 的常量就建立在这些标识上, 所以「命宫有没有紫微星」这类判断,在任何盘面语言的盘上写法与结果都一样。 见 [key 契约](/zh/docs/guide/guides/keys)。 ## 一分钟上手 [#一分钟上手] ```python from x_iztro import Astro astro = Astro() chart = astro.by_solar("2000-8-16", 2, "female") # 2 = 时辰索引,寅时 03:00-05:00 soul = chart.palace("soulPalace") # 命宫 print(chart.five_elements_class, chart.soul, chart.body) print(soul.name, soul.heavenly_stem + soul.earthly_branch) print([s.name for s in soul.major_stars]) ``` ```text 木三局 破军 文昌 命宫 壬午 ['紫微'] ``` 三种编程语言的完整安装与示例见[快速开始](/zh/docs/guide/getting-started)。 ## 从哪读起 [#从哪读起] # 概览 (/zh/docs/guide/getting-started) 排盘要准备哪些输入、每个参数取什么值,以及三种编程语言的安装方式。 *适合:所有人。参数表不需要会写代码也能看懂* 三套绑定共用同一个 Rust 核心,因此参数含义与排盘结果完全一致,只是写法不同。 先看清楚要准备什么,再挑你的编程语言。 ## 你需要准备的输入 [#你需要准备的输入] 无论哪种编程语言,排盘都从这几个参数开始。前三个是必须的,后三个都有默认值。 | 参数 | 含义 | 取值 | | --------------------------- | ------ | --------------------------------------------------------------- | | `solar_date` / `lunar_date` | 出生日期 | `"YYYY-M-D"`,如 `"2000-8-16"`。公历范围 1583–9999 年 | | `time_index` | 出生时辰 | 整数 0–12,见下表 | | `gender` | 性别 | `"male"` / `"female"`(Rust 为 `Gender` 枚举) | | `fix_leap` | 是否修正闰月 | 布尔值,默认 `true` | | `language` | 盘面语言 | `"zh-CN"`(默认) `"zh-TW"` `"en-US"` `"ja-JP"` `"ko-KR"` `"vi-VN"` | | `config` | 分界点与流派 | 见 [Config 详解](/zh/docs/guide/guides/config),缺省即 iztro 默认 | 排盘只需要出生日期、时辰、性别三样。把它们按上面的格式写清楚交出去, 工程师那边一行调用就能出盘。想知道这个库能做什么、典型怎么用, 看[不写代码怎么用它](/zh/docs/guide/guides/for-non-developers)。 ### 时辰索引 [#时辰索引] 紫微斗数按十二时辰计时,且把子时拆成首尾两段,所以索引是 0–12 共 13 个值。 | 索引 | 时辰 | 时间 | 索引 | 时辰 | 时间 | | -- | --- | ----------- | -- | --- | ----------- | | 0 | 早子时 | 00:00–01:00 | 7 | 未时 | 13:00–15:00 | | 1 | 丑时 | 01:00–03:00 | 8 | 申时 | 15:00–17:00 | | 2 | 寅时 | 03:00–05:00 | 9 | 酉时 | 17:00–19:00 | | 3 | 卯时 | 05:00–07:00 | 10 | 戌时 | 19:00–21:00 | | 4 | 辰时 | 07:00–09:00 | 11 | 亥时 | 21:00–23:00 | | 5 | 巳时 | 09:00–11:00 | 12 | 晚子时 | 23:00–24:00 | | 6 | 午时 | 11:00–13:00 | | | | 23:00–24:00 出生的人用索引 `12` 而不是 `0`。这两个索引排出的盘不同: 默认配置下晚子时按**次日**推算日柱,早子时按当日。 这个行为由 `day_divide` 开关控制,见 [Config 详解](/zh/docs/guide/guides/config#晚子时归属-day_divide)。 ### 关于 `fix_leap` [#关于-fix_leap] 农历闰月没有独立的月建,排盘时要决定闰月的日子算上个月还是下个月。 `fix_leap = true`(默认)时按 iztro 的规则修正:闰月前半月算本月,后半月算下月。 设为 `false` 则整个闰月都算本月。 只有出生在闰月的人会受影响,其余情况该参数无作用。 ## 选择编程语言 [#选择编程语言] ## 输入校验 [#输入校验] 给开发者 日期与时辰在**核心层**前置校验,三种编程语言共用同一道防线;非法输入不会 panic, 而是按各自的惯例报错: | 编程语言 | 表现 | | ------ | ------------------------------------------ | | Rust | 返回 `Err(IztroError)`,`.code()` 给出机器可读分类 | | Python | 抛 `IztroError`(继承 `ValueError`),`.code` 同上 | | Go | 返回 `*iztro.Error`,可用 `errors.Is` 匹配哨兵 | | C FFI | 返回 `{"error":"...","code":"..."}` JSON | 核心层校验的是日期格式与真实存在性、公历 1583–9999 范围、时辰索引 0–12。 性别、盘面语言、配置开关这些以字符串传入的参数,在**绑定层**解析时校验 —— Rust 侧它们本来就是枚举,不存在非法取值。 详见[错误处理](/zh/docs/guide/guides/errors)。 # Rust (/zh/docs/guide/getting-started/rust) 安装 x-iztro crate,排出第一张盘,读懂枚举与翻译函数的分工。 *适合:开发者* ## 安装 [#安装] ```bash cargo add x-iztro ``` 或写进 `Cargo.toml`: ```toml [dependencies] x-iztro = "0.2" ``` crate 名为 `x-iztro`,代码中的库名是 `x_iztro`。无 C 依赖,纯 Rust 构建。 ## 排盘 [#排盘] ```rust use x_iztro::{by_solar, IztroError}; use x_iztro::data::types::*; fn main() -> Result<(), IztroError> { let astrolabe = by_solar( "2000-8-16", // 阳历生日 2, // 时辰索引:寅时 Gender::Female, // 性别 true, // fix_leap:修正闰月 Language::ZhCN, // 盘面语言 Config::default(), // 分界点与流派,默认同 JS iztro )?; println!("阳历:{}", astrolabe.solar_date); println!("农历:{}", astrolabe.lunar_date); println!("干支:{}", astrolabe.chinese_date); println!("时辰:{} ({})", astrolabe.time, astrolabe.time_range); Ok(()) } ``` ```text 阳历:2000-8-16 农历:二〇〇〇年七月十七 干支:庚辰 甲申 丙午 庚寅 时辰:寅时 (03:00~05:00) ``` 农历排盘用 [`by_lunar`](/zh/docs/rust/astro#by_lunar):`by_solar` 的 `fix_leap` 在这里换成 三态的 `LeapMonth`(`NotLeap` 非闰月 / `Leap` 闰月 / `LeapFixed` 闰月且十五之后视作次月), 一个参数说清闰月怎么处理,不会把两个布尔写反: ```rust by_lunar("2000-7-17", 2, Gender::Female, LeapMonth::NotLeap, Language::ZhCN, Config::default())?; ``` ## 接下来 [#接下来] 上面的例子只用到了排盘入口。完整的 API——十二宫定位、星耀判断、飞星、 运限、安星模块、数据表、翻译——在 **[Rust API 参考](/zh/docs/rust)** 一栏, 每个函数、类型与方法都有独立条目,附真实运行输出与边界说明。 # Python (/zh/docs/guide/getting-started/python) pip 安装,dataclass 类型化 API,用枚举做与语言无关的判断。 *适合:开发者* ## 安装 [#安装] ```bash pip install x-iztro ``` 要求 Python 3.10 及以上。包内是 PyO3 编译的原生扩展(abi3), 安装后零运行期依赖 —— 不需要 pydantic,也不需要本机的 Rust 工具链。 只有在改动 Rust 侧代码时才需要: ```bash pip install maturin PYO3_USE_ABI3_FORWARD_COMPATIBILITY=1 maturin develop --features python ``` ## 排盘 [#排盘] ```python from x_iztro import Astro astro = Astro() chart = astro.by_solar("2000-8-16", 2, "female") print(chart.solar_date) # 阳历 print(chart.lunar_date) # 农历 print(chart.chinese_date) # 四柱干支 print(chart.time, chart.time_range) print(chart.sign, chart.zodiac) # 星座、生肖 print(chart.soul, chart.body) # 命主、身主 print(chart.five_elements_class) # 五行局 ``` ```text 2000-8-16 二〇〇〇年七月十七 庚辰 甲申 丙午 庚寅 寅时 03:00~05:00 狮子座 龙 破军 文昌 木三局 ``` `solar_date` 原样回显入参字符串,不补零 —— 传 `"2000-08-16"` 就回 `"2000-08-16"`。 要结构化的日期用 `chart.raw_dates`。 农历排盘用 `by_lunar`,比 `by_solar` 多一个 `is_leap_month`。`gender` 之后的参数 (`is_leap_month`、`fix_leap`、`language`、`config`)只能按关键字传入——两个布尔相邻, 位置传参写反了不报错: ```python chart = astro.by_lunar("2000-7-17", 2, "female", is_leap_month=False) ``` 返回的 `Astrolabe` 是 dataclass,字段有类型标注,IDE 能自动补全。 所有文本字段已按盘面语言翻译好。 ## 接下来 [#接下来] 上面的例子只用到了排盘入口。完整的 API——十二宫定位、星耀判断、飞星、 运限、安星模块、数据表、翻译——在 **[Python API 参考](/zh/docs/python)** 一栏, 每个函数、类与方法都有独立条目,附真实运行输出与边界说明。 # Go (/zh/docs/guide/getting-started/go) go get 即用,内嵌 WebAssembly,无 cgo,保留交叉编译能力。 *适合:开发者* ## 安装 [#安装] ```bash go get github.com/x-haose/x-iztro/go/iztro ``` 包里内嵌了核心库编译出的 WebAssembly 模块(`wasm32-wasip1`), 经纯 Go 实现的 [wazero](https://wazero.io) 运行时调用。 这意味着:**不需要 cgo,不需要本机安装 Rust 工具链,交叉编译照常可用**。 wazero 的编译器后端只支持 amd64 与 arm64,其余架构走解释器,速度慢但结果相同。 单个 wasm 实例不能并发使用,包内维护一个实例池(上限 `GOMAXPROCS`), 多 goroutine 调用互不串行化,能真正并行。 首次调用要编译 wasm 模块。编译产物落盘缓存(`os.UserCacheDir()` 下), 所以只有第一次是 \~200ms,之后每个进程的首次调用 \~30ms。 服务进程想让第一个请求就走热路径,在启动时调一次 `iztro.Warmup(ctx)`。 热路径上一次排盘(含 JSON 编解码与内存拷贝)在 0.5ms 量级。 ## 排盘 [#排盘] ```go package main import ( "fmt" "log" "github.com/x-haose/x-iztro/go/iztro" ) func main() { chart, err := iztro.BySolar("2000-8-16", 2, iztro.GenderFemale, true, iztro.LanguageZhCN, nil) if err != nil { log.Fatal(err) } fmt.Println(chart.SolarDate) // 阳历 fmt.Println(chart.LunarDate) // 农历 fmt.Println(chart.ChineseDate) // 四柱干支 fmt.Println(chart.Time, chart.TimeRange) fmt.Println(chart.Sign, chart.Zodiac) fmt.Println(chart.Soul, chart.Body) // 命主、身主 fmt.Println(chart.FiveElementsClass) } ``` ```text 2000-8-16 二〇〇〇年七月十七 庚辰 甲申 丙午 庚寅 寅时 03:00~05:00 狮子座 龙 破军 文昌 木三局 ``` 最后一个参数是 `*Config`,传 `nil` 即取默认配置。`gender` 与 `language` 是具名类型 `iztro.Gender` / `iztro.Language`(`iztro.GenderFemale`、`iztro.LanguageZhCN`,字面量也能直接传)。 农历排盘用 `ByLunar`:`BySolar` 的 `fixLeap` 换成三态的 `iztro.LeapMonth` (`NotLeapMonth` / `LeapMonthKeep` / `LeapMonthFixed`),一个参数说清闰月怎么处理: ```go iztro.ByLunar("2000-7-17", 2, iztro.GenderFemale, iztro.NotLeapMonth, iztro.LanguageZhCN, nil) ``` 每个入口都有 `*Context` 变体(`BySolarContext`、`ByLunarContext` 等), `ctx` 用于取消等待实例池的排队。 ## 错误处理 [#错误处理] 失败一律返回 `*iztro.Error`,带机器可读的 `Code`;用 `errors.Is` 按类别匹配: ```go _, err := iztro.BySolar("2000-13-1", 2, iztro.GenderMale, true, iztro.LanguageZhCN, nil) if errors.Is(err, iztro.ErrInvalidDate) { var e *iztro.Error errors.As(err, &e) fmt.Println(e.Code, e.Message) } ``` ```text invalid_date invalid solar date '2000-13-1': month must be within 1-12 ``` 详见[错误处理](/zh/docs/guide/guides/errors)。 ## 接下来 [#接下来] 上面的例子只用到了排盘入口。完整的 API——十二宫定位、星耀判断、飞星、 运限、安星模块、数据表、翻译——在 **[Go API 参考](/zh/docs/go)** 一栏, 每个导出函数、类型与方法都有独立条目,附真实运行输出与边界说明。 # 一张盘由什么构成 (/zh/docs/guide/concepts) 紫微斗数的最小知识集,不懂代码也能读:一张盘是什么、由哪些部分组成、每一部分决定什么。 *适合:所有人。不懂代码也能读;每页末尾另有「在代码里怎么取」一节* 这一章不教你怎么解读命盘,只解释**一张盘由什么构成**—— 读完你就知道排盘结果里那些名词各自在说什么。 ## 排盘做了什么 [#排盘做了什么] 排盘的输入只有四样东西:出生的**日期**、**时辰**、**性别**,以及一组决定流派与分界点的**配置**。 输出是一张固定形状的盘:**十二个宫格**,每格有自己的干支、宫名, 以及落在里面的若干**星耀**。这张盘一生不变,称为**本命盘**。 在本命盘之上,再按时间推算出**运限**:这一步十年(大限)、这一年(流年)、 这一月、这一日、这一时的宫位与四化各是什么。运限随查询日期变化。 ``` 出生日期 + 时辰 + 性别 + 配置 │ ├─→ 本命盘(十二宫 + 星耀 + 四化) ← 一生固定 │ └─→ 运限(大限/小限/流年/流月/流日/流时)← 随目标日期变化 ``` ## 四个必须先搞清的概念 [#四个必须先搞清的概念] ### 干支 [#干支] 天干十个(甲乙丙丁戊己庚辛壬癸)、地支十二个(子丑寅卯辰巳午未申酉戌亥), 两两配对循环六十次,用来纪年、纪月、纪日、纪时。 出生时刻的四组干支叫**四柱**,也就是排盘结果里那串「庚辰 甲申 丙午 庚寅」。 十二个宫也各有自己的天干(宫干),它是四化飞星的依据。 ### 命宫与身宫 [#命宫与身宫] **命宫**是整张盘的起点,由农历生月与生时定位。十二宫名从命宫开始安放。 **身宫**不是第十三个宫,它是十二宫中的某一个被额外标记,表示后天着力之处。 ### 五行局 [#五行局] 由命宫的干支推出,取值是水二局、木三局、金四局、土五局、火六局之一。 局数(2 到 6)后面要用两次: 1. **起紫微**:用农历日除以局数,定出紫微星落在哪一宫 —— 十四主星的位置全由这一步铺开。 2. **定大限起运岁数**:水二局从虚岁 2 起,火六局从 6 起,此后每宫管十年。 ### 四化 [#四化] 天干各自带一组「化禄、化权、化科、化忌」的指派,指向四颗具体的星。 排盘时,年干的四化会标记在对应的星上;运限层级还各有自己的四化。 这是紫微斗数里动态信息的主要来源,见[四化与飞星](/zh/docs/guide/concepts/mutagen)。 ## 盘面的排列 [#盘面的排列] 紫微斗数的盘是十二个格子围成一圈,每格对应一个**地支**。 排盘结果里的十二宫按固定顺序存放,**第一格是寅宫**: | 位置 | 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | | -- | - | - | - | - | - | - | - | - | - | - | -- | -- | | 地支 | 寅 | 卯 | 辰 | 巳 | 午 | 未 | 申 | 酉 | 戌 | 亥 | 子 | 丑 | 位置对应的是**地支**,不是宫名顺序。命宫可能落在这十二格中的任意一格, 第一格不一定是命宫。要取命宫请按宫名查,不要写死位置。 十二个**宫名**(命宫、父母、福德……)则是排盘算出来的, 从命宫所在的那一格起,逆时针依次安放。所以每张盘的「命宫在哪一格」都可能不同 —— 这正是排盘的第一步。详见[十二宫](/zh/docs/guide/concepts/palaces)。 ## 一张盘上的信息层次 [#一张盘上的信息层次] 逐字段的类型与含义见[数据结构字典](/zh/docs/guide/data-model)。 ## 继续读 [#继续读] # 干支与五行 (/zh/docs/guide/concepts/stems-branches) 天干、地支、五行、阴阳各自决定排盘的哪一部分,宫干从哪来,命主身主怎么查。 *适合:所有人。代码在页尾* 排盘从头到尾都建立在干支之上。看懂这一页,盘上那些两字一组的名词就都有着落了。 ## 十天干与十二地支 [#十天干与十二地支] 天干十个:甲乙丙丁戊己庚辛壬癸。地支十二个:子丑寅卯辰巳午未申酉戌亥。 两者依次配对循环,10 与 12 的最小公倍数是 60,因此一轮是**六十甲子**。 年、月、日、时各配一组干支,合称**四柱**: ```text 庚辰 甲申 丙午 庚寅 年柱 月柱 日柱 时柱 ``` ## 每一柱决定什么 [#每一柱决定什么] 四柱不是并列的装饰,各自驱动排盘的不同部分: | 柱 | 驱动 | | ------ | --------------------------- | | **年柱** | 生年四化、命主身主、十二宫的宫干、大限顺逆、全部年系星 | | **月柱** | 展示用;月份本身(而非月柱)决定命宫与左辅右弼等月系星 | | **日柱** | 展示用;农历日决定紫微起宫与三台八座等日系星 | | **时柱** | 展示用;时辰本身决定命宫、身宫与文昌文曲等时系星 | 换年时点(正月初一还是立春)之所以是个配置开关,正因为年干支牵动的东西最多—— 一旦改变,四化、命主身主、宫干全跟着变。见 [Config 详解](/zh/docs/guide/guides/config)。 ## 阴阳 [#阴阳] 干支各自有阴阳,按序号奇偶交替:甲、丙、戊、庚、壬为阳,乙、丁、己、辛、癸为阴; 子、寅、辰、午、申、戌为阳,丑、卯、巳、未、酉、亥为阴。 六十甲子只配同阴阳的干与支(甲子、乙丑…),所以**一组干支的干与支阴阳必然相同**, 「年干阴阳」与「年支阴阳」永远给出同一个答案。x-iztro 一律按**年支**判定。 阴阳在排盘里只做一件事,但这件事影响很大——**定顺逆**: | 用途 | 规则 | | ----- | -------------------- | | 大限方向 | 性别阴阳与年支阴阳相同则顺行,相异则逆行 | | 长生十二神 | 同上 | | 博士十二神 | 同上 | 性别也算阴阳:男为阳、女为阴。所以「阳男阴女顺行,阴男阳女逆行」这句口诀, 说的就是这三处。小限不在其中——小限的方向只看性别,见[十二宫](/zh/docs/guide/concepts/palaces#小限)。 ## 五行 [#五行] 金木水火土。每个天干、每个地支各自属一行。 两个概念名字像,作用完全不同: * **五行**:单个干支的属性,参考信息 * **五行局**:由**命宫干支**推出的水二局、木三局、金四局、土五局、火六局, 决定紫微起宫与大限起运岁数 做判断时别搞混:前者是干支的 `fiveElements` 字段,后者是星盘的 `five_elements_class`。 ## 对冲 [#对冲] 地支两两相对,相隔六位即为对冲:子午、丑未、寅申、卯酉、辰戌、巳亥。 这正是**对宫**的由来——十二宫排成一圈,本宫与隔六位的那一宫地支恰好对冲, 所以对宫的影响最直接。天干也有对冲(甲庚、乙辛、丙壬、丁癸),戊己居中无冲。 ## 宫位的干支从哪来 [#宫位的干支从哪来] 十二宫的地支是**固定**的:第一格永远是寅宫,最后一格永远是丑宫,从不改变。 天干则由年干经**五虎遁**推出:先定寅宫天干,其余十一宫依次顺排。 以本页这张盘为例,生年干为庚,庚年寅宫起戊,于是十二宫干支是: ```text 戊寅 己卯 庚辰 辛巳 壬午 癸未 甲申 乙酉 丙戌 丁亥 戊子 己丑 ``` 宫干不是摆设——它决定该宫**飞出**哪四颗四化星,是飞星派全部判断的起点。 见[四化与飞星](/zh/docs/guide/concepts/mutagen)。 另有**五鼠遁**,由日干推子时天干,用于定时柱。 ## 命主与身主 [#命主与身主] 每个地支各自对应一颗命主星与一颗身主星,查表即得。两者查表的依据不同: | | 按什么查 | 换命宫重排时 | | ------- | -------- | ------ | | 命主(默认派) | **命宫地支** | 会变 | | 命主(中州派) | **生年地支** | 不变 | | 身主 | **生年地支** | 不变 | `algorithm = zhongzhou` 下命主改按生年地支查,因此换命宫起盘(`rearranged`、 或天盘/地盘/人盘切换)时命主星不再变化。默认派下命主会跟着命宫走。 见 [Config 详解](/zh/docs/guide/guides/config#算法派别-algorithm)。 ## 在代码里怎么取 [#在代码里怎么取] 干支、五行、五虎遁五鼠遁、命主身主全部以数据表形式提供,不必自己抄表。 ```python from x_iztro import data data.heavenly_stems()["jiaHeavenly"].five_elements # 木 data.earthly_branches()["ziEarthly"].yin_yang # 阳 data.earthly_branches()["ziEarthly"].crash # wuEarthly(子午冲) data.heavenly_stems()["wuHeavenly"].crash # None(戊无冲) data.constants().tiger_rule["jiaHeavenly"] # bingHeavenly(甲年寅宫起丙) data.constants().rat_rule["jiaHeavenly"] # jiaHeavenly data.constants().five_elements_class # {'water2nd': 2, ... 'fire6th': 6} zi = data.earthly_branches()["ziEarthly"] zi.soul # tanlangMaj —— 命主 zi.body # huoxingMin —— 身主 ``` 四柱的语言无关标识在 `raw_dates.chinese_date` 下的 `yearly_keys` / `monthly_keys` / `daily_keys` / `hourly_keys`; 顶层 `chinese_date` 是展示串。 | 概念 | 数据表 | 常量 | | ----- | ------------------------- | --------------------------------- | | 天干信息 | `data.heavenly_stems()` | — | | 地支信息 | `data.earthly_branches()` | — | | 五虎遁 | — | `constants().tiger_rule` | | 五鼠遁 | — | `constants().rat_rule` | | 性别阴阳 | — | `constants().gender` | | 五行局局数 | — | `constants().five_elements_class` | 逐字段说明见 [Rust](/zh/docs/rust/data)、[Python](/zh/docs/python/data)、[Go](/zh/docs/go/data) 的数据表一页。 # 十二宫 (/zh/docs/guide/concepts/palaces) 十二个宫名各自代表什么,身宫与来因宫的判定,宫干的用途,以及大限小限如何挂在宫上。 *适合:所有人。代码在页尾* ## 十二宫名 [#十二宫名] 十二宫覆盖人生的十二个领域。它们从**命宫**起,按固定顺序**逆时针**安放在盘上: | 顺序 | 盘上写作 | 常见别名 | 大致含义 | | -- | ---- | ------- | ------------ | | 1 | 命宫 | 本命宫 | 本性、总纲,整张盘的核心 | | 2 | 父母 | 父母宫、相貌宫 | 父母、长辈、上级、庇荫 | | 3 | 福德 | 福德宫 | 精神生活、兴趣、福分 | | 4 | 田宅 | 田宅宫 | 不动产、家庭环境 | | 5 | 官禄 | 官禄宫、事业宫 | 事业、职业、学业 | | 6 | 仆役 | 交友宫、奴仆宫 | 朋友、同事、下属 | | 7 | 迁移 | 迁移宫 | 外出、变动、外界际遇 | | 8 | 疾厄 | 疾厄宫 | 身体、疾病 | | 9 | 财帛 | 财帛宫 | 财务、收入 | | 10 | 子女 | 子女宫 | 子女、晚辈、创造力 | | 11 | 夫妻 | 夫妻宫、配偶宫 | 配偶、亲密关系 | | 12 | 兄弟 | 兄弟宫 | 兄弟姐妹、平辈 | 只有命宫例外。中文盘上第六宫写作**仆役**,不是坊间常见的「交友宫」。 别名列只是各家说法,不会出现在排盘结果里 —— 要做判断请用标识,见页尾。 「宫名的顺序」与「宫在盘上的位置」是两回事。宫名顺序永远是上表这个循环, 但命宫落在哪个地支格由排盘算出,其余十一宫跟着排。 所以十二宫数组的位置对应地支,不对应宫名顺序。 ## 命宫怎么定 [#命宫怎么定] 命宫由**农历生月**与**生时**共同定位:从寅宫起正月顺数到生月,再从该宫起子时逆数到生时。 这个位置决定了整张盘的格局,也是五行局与大限的推算起点。 ## 身宫 [#身宫] 身宫用同样的月时数据但顺数生时得到,落点必定是十二宫中的某一宫。 它不是独立的第十三宫,而是给某个宫加上一个标记。 传统上命宫看先天本质,身宫看后天着力与中年后的走向。 ## 来因宫 [#来因宫] 来因宫是**宫干与出生年干相同**,且不在子、丑二宫的那一宫。 它标示这张盘的「来处」,是飞星派的重要起点。 一张盘上**恒有且仅有一个**来因宫。理由在宫干的安法里: 十二宫的宫干从寅宫起按五虎遁顺排,十个天干排十二个宫, 所以只有开头两个(寅、卯)会在末尾(子、丑)重复一次 —— 与年干相同的宫因此恰好有两个,且必定是「寅或卯」与「子或丑」这一对。 排除子丑之后,剩下的正好一个。 ## 宫干与宫支 [#宫干与宫支] 每个宫都有自己的一组干支: * **宫支**:由宫在盘上的位置固定决定(第一格是寅,见[总览](/zh/docs/guide/concepts#盘面的排列)),一生不变。 * **宫干**:由出生年干按五虎遁推出。 宫干的用途是**四化飞星**:一个宫的宫干决定它「飞出」哪四颗化星, 由此判断宫与宫之间的关系。见[四化与飞星](/zh/docs/guide/concepts/mutagen#飞星)。 ## 大限 [#大限] 大限是十年一步的运程,每个宫掌管一段。起始虚岁由[五行局](/zh/docs/guide/concepts#五行局)决定: | 五行局 | 局数 | 起运虚岁 | 首个大限区间 | | --- | -- | ---- | ------ | | 水二局 | 2 | 2 | 2–11 | | 木三局 | 3 | 3 | 3–12 | | 金四局 | 4 | 4 | 4–13 | | 土五局 | 5 | 5 | 5–14 | | 火六局 | 6 | 6 | 6–15 | 行进方向由**性别阴阳与年支阴阳**共同决定:两者相同则顺行,相异则逆行, 即口诀「阳男阴女顺行,阴男阳女逆行」。 (年干与年支的阴阳恒同,所以按年干说也是同一回事,见[阴阳](/zh/docs/guide/concepts/stems-branches#阴阳)。) 出生到起运虚岁之间的年份不属于任何大限,这段时间用**童限**推算, 由运限接口返回。见[运限](/zh/docs/guide/concepts/horoscope#童限)。 ## 小限 [#小限] 小限是一年一步的运程,与大限并行,每年走一宫,同一个宫每十二年轮到一次。 小限的规则与大限**不同**,两条独立: * **起点**由年支所属的三合组决定(寅午戌年起辰宫、申子辰年起戌宫、 巳酉丑年起未宫、亥卯未年起丑宫) * **方向只看性别**:男顺行、女逆行,与年支阴阳无关 ## 在代码里怎么取 [#在代码里怎么取] 按宫名取宫,判断宫里有什么: ```rust let soul = astrolabe.palace(Palace::Soul).unwrap(); soul.has(&[StarKey::ZiweiMaj]); // 是否同时有这些星 soul.has_one_of(&[StarKey::ZiweiMaj, StarKey::TianfuMaj]); // 是否有任意一颗 soul.has_mutagen(Mutagen::Lu); // 是否有化禄 soul.is_empty(); // 是否空宫(无十四主星) soul.is_body_palace; // 是否身宫 soul.is_original_palace; // 是否来因宫 soul.decadal.range; // 该宫掌管的大限区间,(3, 12) soul.ages; // 小限经过该宫的虚岁列表 ``` ```python from x_iztro.enums import PalaceName, MajorStar, Mutagen soul = chart.palace(PalaceName.SOUL) soul.has([MajorStar.ZIWEI]) soul.has_one_of([MajorStar.ZIWEI, MajorStar.TIANFU]) soul.has_mutagen(Mutagen.LU) soul.is_empty() soul.is_body_palace soul.is_original_palace soul.decadal.range # (3, 12) soul.ages # 例如 [5, 17, 29, 41, 53, 65, 77, 89, 101, 113] ``` ```go soul := chart.Palace(iztro.PalaceSoul) soul.Has(iztro.StarZiweiMaj) soul.HasOneOf(iztro.StarZiweiMaj, iztro.StarTianfuMaj) soul.HasMutagen(iztro.MutagenLu) soul.IsEmpty() soul.IsBodyPalace soul.IsOriginalPalace soul.Decadal.Range soul.Ages ``` 「空宫」指的是没有十四主星,不代表宫里没有任何星 —— 辅星与杂耀通常还在。 空宫在解读上要借对宫的星来看,这也是[三方四正](/zh/docs/guide/concepts/surrounded)存在的原因之一。 ### 宫名的语言无关标识 [#宫名的语言无关标识] 判断用标识,不要匹配宫名文本 —— 换一种盘面语言,匹配文本的分支会静默失效。 | 宫 | 标识 | | -- | ---------------- | | 命宫 | `soulPalace` | | 父母 | `parentsPalace` | | 福德 | `spiritPalace` | | 田宅 | `propertyPalace` | | 官禄 | `careerPalace` | | 仆役 | `friendsPalace` | | 迁移 | `surfacePalace` | | 疾厄 | `healthPalace` | | 财帛 | `wealthPalace` | | 子女 | `childrenPalace` | | 夫妻 | `spousePalace` | | 兄弟 | `siblingsPalace` | 另有两个只能用于查询、不会作为宫名出现的标识:`bodyPalace`(身宫)与 `originalPalace`(来因宫),把它们传给取宫方法即可拿到被标记的那一宫。 Python 的 `PalaceName` 枚举与 Go 的 `Palace*` 常量取值就是上表的标识, 在任何盘面语言的盘上都成立。见 [key 契约](/zh/docs/guide/guides/keys)。 # 星耀 (/zh/docs/guide/concepts/stars) 三组星耀各自装什么,星耀分八类,亮度与四化标记怎么读,四组十二神是什么,以及星名对照表。 *适合:所有人。代码与标识对照表在页尾* 每个宫位上的星耀分三组存放,另外还有四组「十二神」以单值形式挂在宫上。 ## 主星 [#主星] 十四颗主星,是解读命盘的骨架。它们按紫微与天府两个系列的规则安放, 十二宫里有的宫会有两颗,有的一颗都没有(称为**空宫**)。 | 紫微系(六颗) | 天府系(八颗) | | ------- | ------- | | 紫微 | 天府 | | 天机 | 太阴 | | 太阳 | 贪狼 | | 武曲 | 巨门 | | 天同 | 天相 | | 廉贞 | 天梁 | | | 七杀 | | | 破军 | 「空宫」判断的就是这一组是否为空。 ## 辅星 [#辅星] 十四颗辅星,按性质分四类: | 类别 | 成员 | | ------ | ----------------- | | 吉星(六吉) | 左辅、右弼、文昌、文曲、天魁、天钺 | | 煞星(六煞) | 擎羊、陀罗、火星、铃星、地空、地劫 | | 禄存 | 禄存 | | 天马 | 天马 | 它们在传统分法里既非纯吉也非纯煞:禄存主财禄但畏空劫,天马主动主变但需见禄。 判断时常要把它们与六吉六煞分开处理,因此各占一个类别, 筛选时按类别过滤即可,不必按星名硬编码。 ## 杂耀 [#杂耀] 数十颗辅助性质的星,按来源分成年干系、年支系、月系、日系、时系几类,安星依据各不相同。 杂耀分三类: | 类别 | 数量 | 成员 | | ---- | -- | --------------------------------------------------- | | 桃花星 | 4 | 红鸾 `hongluan`、天喜 `tianxi`、天姚 `tianyao`、咸池 `xianchi` | | 解神 | 2 | 解神 `jieshen`、年解 `nianjie` | | 其余杂耀 | 32 | 见下 | 默认派的 32 颗其余杂耀,按性质分组: | 分组 | 成员 | | ----- | ---------------------------------------------------------------------------------------------------------------------------- | | 贵显助力 | 三台 `santai`、八座 `bazuo`、恩光 `engguang`、天贵 `tiangui`、台辅 `taifu`、封诰 `fenggao`、龙池 `longchi`、凤阁 `fengge`、天官 `tianguan`、天福 `tianfu` | | 才艺与庇荫 | 天才 `tiancai`、天寿 `tianshou`、天巫 `tianwu`、天厨 `tianchu`、天德 `tiande`、月德 `yuede`、华盖 `huagai` | | 空亡类 | 天空 `tiankong`、旬空 `xunkong`、截路 `jielu`、空亡 `kongwang` | | 刑忌孤克 | 天刑 `tianxing`、孤辰 `guchen`、寡宿 `guasu`、破碎 `posui`、蜚廉 `feilian`、阴煞 `yinsha`、天哭 `tianku`、天虚 `tianxu`、天月 `tianyue` | | 伤使 | 天伤 `tianshang`、天使 `tianshi` | 桃花星与情感、异性缘、人际吸引力相关,是解读感情与人际时最先看的一组。 四颗里红鸾天喜偏正缘与喜庆,天姚咸池偏情欲与应酬。 默认派安的是**截路**与**空亡**两颗独立的星,不要写成「截空」。 中州派(`algorithm = zhongzhou`)则改安**截空**、**劫杀**、**大耗**、**龙德**, 不安截路与空亡,杂耀总数由 32 变成 34。见 [Config 详解](/zh/docs/guide/guides/config#算法派别-algorithm)。 判断某宫有没有某个四化时,只检查主星与辅星,**不检查杂耀** —— 与 iztro 行为一致。 ## 星耀分八类 [#星耀分八类] 上面三组加起来正好覆盖全部类别: | 类别标识 | 出现在哪一组 | 一句话 | | ----------- | ------ | ---------- | | `major` | 主星 | 十四主星,定盘面骨架 | | `soft` | 辅星 | 六吉 | | `tough` | 辅星 | 六煞 | | `lucun` | 辅星 | 禄存,单独成类 | | `tianma` | 辅星 | 天马,单独成类 | | `flower` | 杂耀 | 桃花星 | | `helper` | 杂耀 | 解神 | | `adjective` | 杂耀 | 其余杂耀 | 判断「某宫见不见煞」这类需求,按类别过滤最稳 —— 换盘面语言星名会变,类别不会。 ## 亮度 [#亮度] 亮度描述一颗星落在某个地支位置上的强弱,共七级: | 盘上写作 | 全称 | 强弱 | | ---- | --- | -- | | 庙 | 庙旺 | 最强 | | 旺 | 旺相 | | | 得 | 得地 | | | 利 | 利益 | | | 平 | 平和 | 中性 | | 不 | 不得地 | | | 陷 | 落陷 | 最弱 | 同一颗星在不同宫位亮度不同,这由星与地支的固定对照表决定。杂耀通常没有亮度。 英文、韩文等语言的词表没有亮度译名,输出的是 `[+3]`(庙)到 `[-3]`(陷)的记号。 判断亮度请用标识而非文本,见[多语言输出](/zh/docs/guide/guides/i18n)。 ## 四组十二神 [#四组十二神] 除了三组星耀,每个宫还挂着四个**单值**字段,各自来自一套十二神的循环安法。 每组的十二个成员排满十二宫,每宫恰好一个。 ### 长生十二神 [#长生十二神] 依据**五行局与阴阳男女**起。描述一件事从萌生到消亡再重来的十二个阶段。 长生 `changsheng` / 沐浴 `muyu` / 冠带 `guandai` / 临官 `linguan` / 帝旺 `diwang` / 衰 `shuai` / 病 `bing` / 死 `si` / 墓 `mu` / 绝 `jue` / 胎 `tai` / 养 `yang` ### 博士十二神 [#博士十二神] 依据**禄存位置与阴阳男女**起。偏重才能、财禄与是非。 博士 `boshi` / 力士 `lishi` / 青龙 `qinglong` / 小耗 `xiaohao` / 将军 `jiangjun` / 奏书 `zhoushu` / 飞廉 `faylian` / 喜神 `xishen` / 病符 `bingfu` / 大耗 `dahao` / 伏兵 `fubing` / 官府 `guanfu` ### 岁前十二神 [#岁前十二神] 依据**年支**起,恒顺行。偏重一年之内的吉凶事件。 岁建 `suijian` / 晦气 `huiqi` / 丧门 `sangmen` / 贯索 `guansuo` / 官符 `gwanfu` / 小耗 `xiaohao` / 大耗 `dahao` / 龙德 `longde` / 白虎 `baihu` / 天德 `tiande` / 吊客 `diaoke` / 病符 `bingfu` 中州派把这一组里的**大耗**换成**岁破**(`suipo`)。 ### 将前十二神 [#将前十二神] 依据**年支三合组**起,恒顺行。偏重动向、驿马与人事阻碍。 将星 `jiangxing` / 攀鞍 `panan` / 岁驿 `suiyi` / 息神 `xiishen` / 华盖 `huagai` / 劫煞 `jiesha` / 灾煞 `zhaisha` / 天煞 `tiansha` / 指背 `zhibei` / 咸池 `xianchi` / 月煞 `yuesha` / 亡神 `wangshen` `faylian`(飞廉)、`gwanfu`(官符)、`xiishen`(息神)、`zhaisha`(灾煞) 这几个标识与拼音不一致 —— 它们沿用 iztro 的原始词表键名,用来与同音的 `feilian`(蜚廉,杂耀)、`guanfu`(官府,博士十二神)、`xishen`(喜神,博士十二神)区分。 照抄标识,不要按拼音自己拼。 运限层级还会带自己的岁前十二神与将前十二神,它们按运限年支重新起算, 与本命盘上的这四组是两回事。见[运限](/zh/docs/guide/concepts/horoscope)。 ## 在代码里怎么取 [#在代码里怎么取] ### 遍历一个宫的星耀 [#遍历一个宫的星耀] ```python soul = chart.palace("soulPalace") for s in soul.major_stars + soul.minor_stars + soul.adjective_stars: print(s.key, s.name, s.type, s.brightness, s.mutagen) ``` ```text ziweiMaj 紫微 major 庙 None wenquMin 文曲 soft 陷 None fengge 凤阁 adjective None None tianfu 天福 adjective None None jielu 截路 adjective None None feilian 蜚廉 adjective None None nianjie 年解 helper None None ``` 单颗星的字段: | 字段 | 含义 | | ------------------------------ | ------------------------------------------- | | `name` | 星名,按盘面语言翻译 | | `key` | 语言无关标识,如 `ziweiMaj` | | `type` | 八个类别之一 | | `scope` | 所属层级:`origin` 本命、`decadal` 大限、`yearly` 流年…… | | `brightness` / `brightnessKey` | 亮度,只有部分星有 | | `mutagen` / `mutagenKey` | 四化标记,只有被化到的星有 | ### 四组十二神 [#四组十二神-1] ```python for p in chart.palaces: print(p.name, p.changsheng12, p.boshi12, p.suiqian12, p.jiangqian12) ``` 每个字段另有 `*_key` 版本(`changsheng12_key` 等),判断用它。 ### 查一颗星在哪 [#查一颗星在哪] ```rust if let Some(star) = astrolabe.star(StarKey::ZiweiMaj) { // StarRef 解引用即 Star,另可取所在宫 println!("{:?} {:?}", star.brightness, star.palace().name); } ``` ```python from x_iztro.enums import MajorStar star = chart.star(MajorStar.ZIWEI) # 只要星 star, palace = chart.star_in_palace(MajorStar.ZIWEI) # 连所在宫一起 ``` ```go star, palace := chart.Star(iztro.StarZiweiMaj) ``` 查找会遍历三组,所以主星、辅星、杂耀都能查到;查不到返回空值。 ### 星名对照表 [#星名对照表] 同一颗星在不同盘面语言下文本不同,标识不变。英文词表来自 iztro 原词表, 是意译而非音译,也不是英文命理界的通行译法 —— 只做展示,判断一律用标识。 | 标识 | 中文 | 英文 | | -------------- | -- | --------- | | `ziweiMaj` | 紫微 | emperor | | `tianjiMaj` | 天机 | advisor | | `taiyangMaj` | 太阳 | sun | | `wuquMaj` | 武曲 | general | | `tiantongMaj` | 天同 | fortunate | | `lianzhenMaj` | 廉贞 | judge | | `tianfuMaj` | 天府 | empress | | `taiyinMaj` | 太阴 | moon | | `tanlangMaj` | 贪狼 | wolf | | `jumenMaj` | 巨门 | advocator | | `tianxiangMaj` | 天相 | minister | | `tianliangMaj` | 天梁 | sage | | `qishaMaj` | 七杀 | marshal | | `pojunMaj` | 破军 | rebel | | `zuofuMin` | 左辅 | officer | | `youbiMin` | 右弼 | helper | | `wenchangMin` | 文昌 | scholar | | `wenquMin` | 文曲 | artist | | `tiankuiMin` | 天魁 | assistant | | `tianyueMin` | 天钺 | aide | | `qingyangMin` | 擎羊 | driven | | `tuoluoMin` | 陀罗 | tangled | | `huoxingMin` | 火星 | impulsive | | `lingxingMin` | 铃星 | spark | | `dikongMin` | 地空 | ideologue | | `dijieMin` | 地劫 | fickle | | `lucunMin` | 禄存 | money | | `tianmaMin` | 天马 | horse | 杂耀与十二神的标识见上文各节。任意标识与任意语言之间的换算用翻译与反查函数, 见[多语言输出](/zh/docs/guide/guides/i18n#标识与译名的换算)。 # 四化与飞星 (/zh/docs/guide/concepts/mutagen) 禄权科忌从哪来、十天干四化全表、自化与飞化怎么判断。 *适合:所有人。代码与完整方法表在页尾* 四化是紫微斗数里最重要的动态信息。同一张盘,四化把静态的星耀连成有方向的关系网。 ## 四种化 [#四种化] | 四化 | 标识 | 常见理解 | | -- | ----------- | -------- | | 化禄 | `sihuaLu` | 顺遂、收获、缘起 | | 化权 | `sihuaQuan` | 掌控、扩张、力度 | | 化科 | `sihuaKe` | 名声、贵人、缓和 | | 化忌 | `sihuaJi` | 阻滞、执着、变数 | ## 四化从天干来 [#四化从天干来] 每个天干固定指派四颗星,分别化禄、化权、化科、化忌。这是一张查表: | 天干 | 化禄 | 化权 | 化科 | 化忌 | | -- | -- | -- | -- | -- | | 甲 | 廉贞 | 破军 | 武曲 | 太阳 | | 乙 | 天机 | 天梁 | 紫微 | 太阴 | | 丙 | 天同 | 天机 | 文昌 | 廉贞 | | 丁 | 太阴 | 天同 | 天机 | 巨门 | | 戊 | 贪狼 | 太阴 | 右弼 | 天机 | | 己 | 武曲 | 贪狼 | 天梁 | 文曲 | | 庚 | 太阳 | 武曲 | 太阴 | 天同 | | 辛 | 巨门 | 太阳 | 文曲 | 文昌 | | 壬 | 天梁 | 紫微 | 左辅 | 武曲 | | 癸 | 破军 | 巨门 | 太阴 | 贪狼 | 庚干化科历来有太阴、天府、天同等不同说法。x-iztro 采用**太阴化科**,与 JS iztro 一致。 `algorithm` 开关**不改四化表** —— 中州派与默认派用的是同一张表。 要换成别的取法,走 Config 的自定义四化表:整表替换某个天干的四化, 见 [Config 详解](/zh/docs/guide/guides/config#自定义四化表与亮度表)。 ## 本命四化 [#本命四化] 排盘时用**出生年干**查上表,把四化标记打在对应的星上。 一张盘上的本命四化恰好四个 —— 被化的星里除了十四主星,还有文昌、文曲、左辅、右弼, 这四颗辅星一定在盘上,所以总数不会少。 ## 运限四化 [#运限四化] 除了本命,每个运限层级还有自己的四化:大限用大限宫干、流年用流年干, 依次类推。它们叠加在同一张盘上,是运限解读的主要抓手。 返回的四颗星顺序固定为**禄、权、科、忌**。 ## 飞星 [#飞星] 宫位也有天干(宫干)。用宫干查四化表,就得到这个宫「飞出去」的四颗星, 再看这四颗星落在哪个宫 —— 这就是**飞星**,用来描述宫与宫之间的作用关系。 ## 自化 [#自化] 如果一个宫的宫干飞出的化星,正好就落在这个宫自己里面,称为**自化**。 自化在解读上意味着力量在本宫内部消耗或外泄,与飞入他宫的性质不同。 ## 在代码里怎么取 [#在代码里怎么取] ### 读本命四化 [#读本命四化] ```python for p in chart.palaces: for s in p.major_stars + p.minor_stars: if s.mutagen: print(f"{p.name} 的 {s.name} 化{s.mutagen}") ``` ```text 财帛 的 武曲 化权 子女 的 太阳 化禄 仆役 的 太阴 化科 疾厄 的 天同 化忌 ``` ### 读运限四化 [#读运限四化] ```python h = chart.horoscope("2024-10-1", 0) print(h.decadal.mutagen) # 大限四化的四颗星 print(h.yearly.mutagen) # 流年四化的四颗星 ``` ```text ['太阳', '武曲', '太阴', '天同'] ['廉贞', '破军', '武曲', '太阳'] ``` 判断类方法另有 `*_keys` 版本(`h.yearly.mutagen_keys`),判断用它。 ### 判断某宫有没有某个四化 [#判断某宫有没有某个四化] ```python soul.has_mutagen(Mutagen.LU) soul.not_have_mutagen(Mutagen.JI) ``` ### 飞星判断 [#飞星判断] ```rust let wealth = astrolabe.palace(Palace::Wealth).unwrap(); // 财帛宫的宫干化忌,是否落在命宫 wealth.flies_to(Palace::Soul, &[Mutagen::Ji]); ``` ```python soul.flies_one_of_to(PalaceName.WEALTH, [Mutagen.LU, Mutagen.QUAN]) soul.not_fly_to(0, Mutagen.JI) places = soul.mutaged_places() # 禄权科忌各自飞入了哪个宫,长度为 4 ``` ### 自化判断 [#自化判断] ```rust soul.self_mutaged(&[Mutagen::Lu]); // 命宫是否自化禄 soul.self_mutaged_one_of(&[]); // 是否有任意一种自化 soul.not_self_mutaged(&[]); // 四种自化都没有 ``` `self_mutaged_one_of` 与 `not_self_mutaged` 传空即检查全部四化, 也可以只检查其中几种。 ### 完整方法一览 [#完整方法一览] | 方法 | 作用 | | ----------------------------- | ----------------- | | `has_mutagen(m)` | 本宫是否有指定四化 | | `not_have_mutagen(m)` | 本宫是否没有指定四化 | | `mutagen_stars(ms)` | 本宫宫干在指定四化位上对应的星 | | `flies_to(target, ms)` | 指定化星是否**全部**落在目标宫 | | `flies_one_of_to(target, ms)` | 是否有**任一颗**落在目标宫 | | `not_fly_to(target, ms)` | 是否**一颗都不**落在目标宫 | | `self_mutaged(ms)` | 本宫是否自化指定四化 | | `self_mutaged_one_of(ms?)` | 本宫是否有任一自化 | | `not_self_mutaged(ms?)` | 本宫指定自化全无 | | `mutaged_places()` | 禄权科忌四星各自所在的宫 | 宫上另有 `mutagen_star_keys` 字段,直接给出本宫宫干化出的四颗星的标识, 顺序为禄、权、科、忌;用了自定义四化表时它也会跟着变。 这是复刻 iztro 的行为,三种编程语言一致:`flies_to` 在四化列表为空时返回 `false`, 而 `flies_one_of_to` 与 `not_fly_to` 在同样情况下返回 `true`。 三方四正范围内的四化判断见[三方四正](/zh/docs/guide/concepts/surrounded)。 # 三方四正 (/zh/docs/guide/concepts/surrounded) 为什么一个宫不能单独看,三方四正由哪四个宫组成,以及在三种编程语言里怎么取。 *适合:所有人。代码与判断方法表在页尾* ## 为什么需要它 [#为什么需要它] 单看一个宫会漏掉一半信息。紫微斗数的惯例是:解读任何一宫,都要连着它的 **对宫**与两个**三合宫**一起看。这四个宫合称**三方四正**。 最直观的理由是空宫 —— 一个宫里没有主星时,传统上要「借对宫的星来看」。 即便不是空宫,三方四正里的煞星与四化同样会影响本宫的判断。 ## 由哪四个宫组成 [#由哪四个宫组成] 以本宫在盘上的位置为基准,另外三宫的位置是固定的: | 成员 | 位置 | 说明 | | --- | ------- | --------- | | 本宫 | `i` | 要看的那个宫 | | 对宫 | `i + 6` | 正对面,影响最直接 | | 官禄位 | `i + 4` | 三合之一 | | 财帛位 | `i + 8` | 三合之一 | 位置对 12 取模。这四个位置在盘上构成一个三角加一条对角线, 「三方」指三合的三个宫,「四正」指加上对宫共四个。 ``` i+4 (官禄位) / \ / \ i ──────── i+6 (对宫) \ / \ / i+8 (财帛位) ``` 「官禄位」与「财帛位」是**相对本宫**的称呼,不是盘上的官禄宫与财帛宫。 以命宫为本宫时它们才恰好是官禄宫和财帛宫;以其他宫为本宫时只是位置关系相同。 ## 在代码里怎么取 [#在代码里怎么取] 三种编程语言都既接受宫位索引,也接受宫名: ```rust let sp = astrolabe.surrounded_palaces(Palace::Soul).unwrap(); println!("{:?}", sp.opposite.name); ``` ```python sp = chart.surrounded_palaces(PalaceName.SOUL) sp = chart.surrounded_palaces(soul.index) ``` ```go sp := chart.SurroundedPalaces(iztro.PalaceSoul) // 按宫名 sp = chart.SurroundedPalacesByIndex(soul.Index) // 按索引 ``` 四个成员分别是 `target`(本宫)、`opposite`(对宫)、`career`(官禄位)、 `wealth`(财帛位)。 ### 判断方法 [#判断方法] 三方四正的判断方法与单宫同名,但检查范围是四个宫的并集,三种编程语言一致: | 方法 | 作用 | | --------------------- | ------------------- | | `have(stars)` | 四宫合起来是否包含**全部**指定星耀 | | `have_one_of(stars)` | 是否包含**任意一颗** | | `not_have(stars)` | 是否**一颗都不包含** | | `have_mutagen(m)` | 四宫中是否有任一宫带指定四化 | | `not_have_mutagen(m)` | 四宫都没有指定四化 | ```go sp := chart.SurroundedPalaces(iztro.PalaceSoul) sp.Have(iztro.StarTianfuMaj) // 三方四正有天府 sp.HaveOneOf(iztro.StarQingyangMin, iztro.StarTuoluoMin) // 是否见羊陀 sp.NotHaveMutagen(iztro.MutagenJi) // 是否不见化忌 ``` 星盘上还有三个直接判断的快捷方法,省去先取三方四正这一步: ```python chart.is_surrounded(PalaceName.SOUL, [MajorStar.TIANFU]) chart.is_surrounded_one_of(PalaceName.SOUL, [MinorStar.QINGYANG, MinorStar.TUOLUO]) chart.not_surrounded(PalaceName.SOUL, [MinorStar.HUOXING]) ``` `have` 要求列表里的星**全部**出现,`have_one_of` 只要求出现一个。 判断「见煞」这类需求几乎总是用 `have_one_of`。 ### 一个实际例子 [#一个实际例子] 判断命宫格局是否「吉星拱照且不见煞」: ```python from x_iztro.enums import PalaceName, MinorStar sp = chart.surrounded_palaces(PalaceName.SOUL) lucky = sp.have_one_of([MinorStar.ZUOFU, MinorStar.YOUBI, MinorStar.WENCHANG, MinorStar.WENQU]) clean = sp.not_have([MinorStar.QINGYANG, MinorStar.TUOLUO, MinorStar.HUOXING, MinorStar.LINGXING]) print(lucky, clean, lucky and clean) ``` ```text True False False ``` 这张盘的三方四正里有文昌文曲,但也见了羊陀火铃中的某几颗,所以不成立。 星耀的分类与标识见[星耀](/zh/docs/guide/concepts/stars)。 # 运限 (/zh/docs/guide/concepts/horoscope) 六个时间层级各自算什么,同一格子的宫名为什么会变,童限怎么起,流耀与流年十二神从哪来。 *适合:所有人。代码在页尾* 本命盘一生不变,**运限**是把时间叠加到本命盘上得到的动态信息。 给一个目标日期,x-iztro 一次返回六个层级。 ## 同一个格子,宫名会变 [#同一个格子宫名会变] 这是运限里最容易绕住人的一点,先说清楚: 运限解读要把运限所在的那一宫当作「这一步的命宫」,其余十一宫跟着重排。 所以**同一个格子,在本命盘上是财帛宫,在某个大限里可能就是命宫**。 每个运限层级都给出一份重排后的十二宫名,按盘上位置排列。 本命盘的宫名不受影响,两套并存 —— 读结果时要分清手上拿的是哪一套。 ## 六个层级 [#六个层级] | 层级 | 周期 | 依据 | | -- | --- | ----------------------- | | 大限 | 十年 | 五行局起运虚岁 + 性别与年支阴阳定的行进方向 | | 小限 | 一年 | 年支三合定起点,性别定方向 | | 流年 | 一年 | 目标日期所在的农历年 | | 流月 | 一月 | 目标日期所在的农历月 | | 流日 | 一日 | 目标日期 | | 流时 | 一时辰 | 目标时辰索引 | 大限与小限都是「按年龄推进」的,但规则完全不同,各走各的; 流年到流时则是「按日历推进」的。两套并行,共同构成一次查询的结果。 ## 每个层级有什么 [#每个层级有什么] 除小限与流年外,每个层级的结构相同: | 字段 | 含义 | | ---------------------------------- | ------------------------- | | `index` | 该运限落在哪一个盘上位置(0–11,第一格是寅宫) | | `name` | 层级名,按盘面语言翻译(「大限」「流年」……) | | `heavenly_stem` / `earthly_branch` | 该运限的干支 | | `palace_names` | 以该运限位置为命宫重排的十二宫名 | | `mutagen` | 该运限干引发的四化星,顺序为禄、权、科、忌 | | `stars` | 流耀在十二宫的分布,没有流耀的层级为空 | 小限额外带**虚岁**(`nominal_age`)。 流年额外带**流年十二神**:按流年支重起的岁前十二神与将前十二神。 这两组与本命盘上宫位自带的那两组是两回事 —— 本命的按出生年支起,流年的按目标年支起。 ## 童限 [#童限] 大限从五行局决定的虚岁才开始(水二局 2 岁、火六局 6 岁)。 在此之前的年份不属于任何大限,这段时间用**童限**推算。 童限按虚岁在六个宫之间循环,口诀是「一命二财三疾厄,四妻五福六官禄」: | 虚岁 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | … | | --- | -- | -- | -- | -- | -- | -- | -- | -- | -- | | 童限宫 | 命宫 | 财帛 | 疾厄 | 夫妻 | 福德 | 官禄 | 命宫 | 财帛 | 循环 | 当目标日期落在起运之前,大限字段返回的就是童限,层级名显示为「童限」。 字段结构不变,所以调用方不需要特殊处理,只在需要区分时看层级名。 ## 流耀 [#流耀] 流年、流月等层级会带一批只在该层级存在的星,称为**流耀** (运昌、运曲、运魁、运钺、运鸾、运喜、运禄、运羊、运陀、运马, 以及流年层的流昌、流曲……)。它们按十二宫分组存放。 星的所属层级字段标明它属于哪一层:本命星是 `origin`,大限流耀是 `decadal`, 流年流耀是 `yearly`。 ## 分界点会改变结果 [#分界点会改变结果] 运限的干支与虚岁受两个配置开关影响: * `horoscope_divide` 决定运限年按正月初一还是立春换年,以及流月按初一还是节气分界 * `age_divide` 决定虚岁按农历年跨年加一,还是过了生日才加一 在年初或生日前后查询时,这两个开关会直接改变返回的干支与虚岁。 详见 [Config 详解](/zh/docs/guide/guides/config)。 ## 在代码里怎么取 [#在代码里怎么取] 运限从已排好的盘发起,出生参数、盘面语言与配置都从盘上取,只需给目标日期与时辰: ```python h = chart.horoscope("2024-10-1", 0) ``` ```rust let h = astrolabe.horoscope("2024-10-1", 0)?; ``` ```go h, err := astrolabe.Horoscope("2024-10-1", 0) ``` 读六个层级: ```python h = chart.horoscope("2024-10-1", 0) print(h.decadal.name, h.decadal.heavenly_stem + h.decadal.earthly_branch, h.decadal.mutagen) print(h.age.name, "虚岁", h.age.nominal_age) print(h.yearly.name, h.yearly.heavenly_stem + h.yearly.earthly_branch) print("流年重排宫名:", h.yearly.palace_names) print("流年岁前十二神:", h.yearly.yearly_dec_star.suiqian12) ``` ```text 大限 庚辰 ['太阳', '武曲', '太阴', '天同'] 小限 虚岁 25 流年 甲辰 流年重排宫名: ['夫妻', '兄弟', '命宫', '父母', '福德', '田宅', '官禄', '仆役', '迁移', '疾厄', '财帛', '子女'] 流年岁前十二神: ['吊客', '病符', '岁建', '晦气', '丧门', '贯索', '官符', '小耗', '大耗', '龙德', '白虎', '天德'] ``` 遍历流耀: ```python for palace_index, stars in enumerate(h.yearly.stars or []): for s in stars: print(palace_index, s.name, s.scope) ``` 小限与流年各自多带一项专属数据(虚岁、流年十二神),Rust 里通用字段收在 `.base` 下, 但两个类型都实现了 `Deref`,`h.yearly.heavenly_stem` 直接可读;序列化到 Python / Go 时 这一层被展平,同样写 `h.yearly.heavenly_stem`。 # 排盘是怎么算的 (/zh/docs/guide/concepts/how-it-works) 从出生数据到一张完整命盘的九个步骤,每一步在做什么、依据是什么。 *适合:所有人。想按步骤调 API 的看[排盘九步对应的 API](/zh/docs/guide/guides/step-api)* 排盘不是玄学,是一串确定的推算。给定同样的出生数据与同样的流派取舍, 任何人算出来的盘都该一模一样。这一页把这串推算拆成九步。 日常排盘不需要自己走这些步骤——直接用排盘入口即可。 读这一页是为了知道结果从哪来,以及某个字段为什么长这样。 ## 全流程 [#全流程] **公历转农历** —— 得到农历年月日与四柱干支 **定月索引** —— 处理闰月归属 **定命宫身宫** —— 由月索引与时辰推出 **定五行局** —— 由命宫干支推出 **起紫微天府** —— 由五行局与农历日推出 **安十四主星** —— 依紫微天府的位置铺开 **安辅星与杂耀** —— 各按年干、年支、月、日、时起 **安四组十二神** —— 长生、博士、岁前、将前 **推大限小限** —— 由五行局、性别、年支决定 *** ## 逐步说明 [#逐步说明] ### 1. 公历转农历 [#1-公历转农历] 斗数以农历为基础,但输入通常是公历。这一步同时得到年、月、日、时四柱干支。 年干支的换算时点受配置影响:正月初一与立春之间出生的人,两种配置得到不同的年干支。 年系杂耀另按一个独立的开关取年支——两个开关可以分别设, 因此年系杂耀与主星可能基于不同的年支。这是 iztro 的实际行为,x-iztro 原样复刻。 ### 2. 定月索引 [#2-定月索引] 闰月十五日之后按下月算(可以关掉),晚子时不参与这项修正。 ### 3. 定命宫身宫 [#3-定命宫身宫] 从寅宫起正月,顺数到生月,再从生月逆数到生时——落点即**命宫**。 **身宫**用同样的起点但顺数生时。命宫的天干由五虎遁从年干推得。 ### 4. 定五行局 [#4-定五行局] 由**命宫干支**查表得出,五种取值:水二局、木三局、金四局、土五局、火六局。 局数(2/3/4/5/6)后面要用两次:起紫微时做除数,推大限时做起运岁数。 地盘改用身宫干支起局,人盘改用福德宫干支。起局干支一变, 后面第 5、6、8、9 步全部重算——这就是排盘视角配置做的事。 ### 5. 起紫微天府 [#5-起紫微天府] 用农历日除以局数,按「起紫微星诀」定出紫微的落宫。 天府与紫微关于寅申一线互为镜像:天府位 = 12 − 紫微位(对 12 取模)。 ### 6. 安十四主星 [#6-安十四主星] 紫微系六颗按固定间隔**逆时针**从紫微位铺开,天府系八颗**顺时针**从天府位铺开。 间隔不是连续的,中间有空位: | 紫微系(逆行,从紫微位起) | 退几位 | | ------------- | --- | | 紫微 | 0 | | 天机 | 1 | | 太阳 | 3 | | 武曲 | 4 | | 天同 | 5 | | 廉贞 | 8 | | 天府系(顺行,从天府位起) | 进几位 | | ------------- | --- | | 天府 | 0 | | 太阴 | 1 | | 贪狼 | 2 | | 巨门 | 3 | | 天相 | 4 | | 天梁 | 5 | | 七杀 | 6 | | 破军 | 10 | 两系铺完,十四主星的位置就全定了。生年干的四化标记也在这一步打上。 ### 7. 安辅星与杂耀 [#7-安辅星与杂耀] 各组的起法依据不同: | 组 | 依据 | 例 | | ----------- | ------- | ------------- | | 禄存、擎羊、陀罗 | 年干 | 「甲禄到寅宫」,禄前羊后陀 | | 天马 | 年支 | 只落四马地(寅申巳亥) | | 天魁、天钺 | 年干 | | | 左辅、右弼 | 农历月 | 「辰上顺正寻左辅」 | | 文昌、文曲 | 时支 | 「戌上逆时觅文昌」 | | 火星、铃星 | 年支 + 时支 | | | 地空、地劫 | 时支 | 亥宫起子时,地空逆、地劫顺 | | 三台、八座、恩光、天贵 | 农历日 | 由辅星位置起初一数 | | 年系杂耀 | 年干或年支 | 数量最多 | 四颗共用同一个农历日数,但起点与方向各不相同: 三台从左辅位**顺**数、八座从右弼位**逆**数、恩光从文昌位顺数、天贵从文曲位顺数 (恩光天贵再各退一位)。这是 iztro 的实际算法,x-iztro 原样复刻。 ### 8. 安四组十二神 [#8-安四组十二神] 每组十二个标记排满十二宫,每宫恰好一个: | 组 | 起点 | 顺逆 | | ----- | ------------ | ------- | | 长生十二神 | 五行局定(水二局在申…) | 性别与年支阴阳 | | 博士十二神 | 禄存所在宫 | 同上 | | 岁前十二神 | 年支所在宫 | 恒顺行 | | 将前十二神 | 年支三合组定 | 恒顺行 | ### 9. 推大限小限 [#9-推大限小限] 大限从命宫起,每宫十年,起运岁数即局数(水二局 2 岁起,木三局 3 岁起…); 顺逆由性别阴阳与年支阴阳决定。 小限另起一套:从年支所属三合组定的宫起,按虚岁逐年走一宫, 方向只看性别(男顺女逆)。 *** ## 运限是另一条线 [#运限是另一条线] 以上九步排出的是**本命盘**,一次算完就固定了。运限是把本命盘投影到某个时间点: 按目标日期算出该层级的干支,据此重排十二宫名、算出该层级的四化与流耀。 本命盘不变,变的是「此刻站在哪一宫看」。见[运限](/zh/docs/guide/concepts/horoscope)。 ## 想按步骤调 API [#想按步骤调-api] 九步在 x-iztro 里每一步都有对应的公开函数,可以只取中间某一步的结果。 对照表与「哪些步骤会被配置改变」见 [排盘九步对应的 API](/zh/docs/guide/guides/step-api)。 # 概览 (/zh/docs/guide/guides) AI 解盘、配置与流派、跨语言判断、多种盘面语言输出、错误处理与扩展的使用指南。 *适合:所有人。每张卡片标了适合的读者* 装好之后会遇到的实际问题都在这一章。 ## 最容易踩的三个坑 [#最容易踩的三个坑] 1. **用星名做判断**。换一种盘面语言,所有分支静默失效。 用[语言无关标识](/zh/docs/guide/guides/keys)。 2. **忽略晚子时**。23:00–24:00 出生要用时辰索引 `12` 而不是 `0`, 两者排出的盘不同。见 [Config](/zh/docs/guide/guides/config#晚子时归属-day_divide)。 3. **宫位位置当成宫名顺序**。十二宫数组的第一格是寅宫,不是命宫。 见[十二宫](/zh/docs/guide/concepts/palaces)。 # 不写代码怎么用它 (/zh/docs/guide/guides/for-non-developers) x-iztro 能做什么、典型用在哪、要跟工程师交代哪几件事,以及它准不准。 *适合:命理爱好者 · 产品与决策者。全页无需读代码* ## 这是什么 [#这是什么] x-iztro 是一个**排盘引擎**:给它出生日期、时辰、性别,它算出一张完整的紫微斗数命盘, 并能把这张盘转成一段大模型读得懂的文字。 它不是一个 App,也不是一个网站 —— 它是给程序用的一段代码, 需要由工程师装进你们自己的产品里。 ## 它能做什么 [#它能做什么] * **排一张完整的本命盘**:十二宫的位置与干支、每个宫里落了哪些星、 星的亮度与四化、四组十二神、大限与小限 * **算任意时间点的运限**:大限、小限、流年、流月、流日、流时六个层级 * **一键转成 AI 能读的文字**:不必自己描述盘面, 生成的文本直接贴给大模型就能开始问 * **六种盘面语言**:简体中文、繁体中文、英文、日文、韩文、越南文 * **流派可切换**:年分界点、晚子时归属、中州派、天地人三盘,都是配置项 它**不做**解读。星耀落宫算得出来,「这个人事业如何」不在库的职责里 —— 那一步交给 AI 或人。 ## 典型用在哪 [#典型用在哪] ### AI 解盘机器人 [#ai-解盘机器人] 最常见的一种。用户在对话里报生日,后台调 x-iztro 排盘, 把生成的盘面文字连同用户的问题一起发给大模型,模型给出解读。 盘由库算准,话由模型来说 —— 两件事分开,各自可靠。 ### 命理 App 的后端 [#命理-app-的后端] App 前端负责画盘、做交互,排盘计算放在服务端。 同一份计算逻辑可以同时服务 iOS、Android、Web 和小程序。 ### 批量数据分析 [#批量数据分析] 比如「这十万条出生数据里,命宫有紫微的占多少」「某个格局与某个字段的相关性」。 x-iztro 排一张盘是毫秒级的,几十万条数据在单机上跑得完。 ## 需要找工程师吗 [#需要找工程师吗] 需要。x-iztro 是一个代码库,不是可以直接打开的软件。 但工作量很小:装上它、写三行调用、把结果接到你们已有的流程里。 一个熟练的后端工程师,做出一个能跑的原型通常在半天以内。 ## 要跟工程师说什么 [#要跟工程师说什么] 把下面这几条转给他们就够了: | 事项 | 内容 | | ----- | -------------------------------------------------------------------------------------- | | 仓库 | [github.com/x-haose/x-iztro](https://github.com/x-haose/x-iztro) | | 编程语言 | Rust、Python、Go 三选一,结果完全一致 | | 安装 | Python 用 `pip install x-iztro`;Go 用 `go get`;Rust 用 `cargo add` | | 环境要求 | Python 3.10 及以上 / Go 1.22 及以上 / Rust edition 2024 | | 输入 | 出生日期、时辰索引(0–12)、性别,三样 | | AI 用法 | 调 `astrolabe_to_prompt` 拿到盘面文字,直接喂大模型 | | 文档 | [快速开始](/zh/docs/guide/getting-started)、[AI Prompt 生成](/zh/docs/guide/guides/ai-prompt) | 不是小时数,是 0–12 的十三个值:0 是早子时(00:00–01:00), 1 丑时、2 寅时……11 亥时,12 是晚子时(23:00–24:00)。 对照表见[你需要准备的输入](/zh/docs/guide/getting-started#时辰索引)。 **23 点以后出生的要填 12,不是 0** —— 这两个值排出的盘不一样。 ## 它准不准 [#它准不准] 「准」在这里有一个很具体的定义:**与 JS 版的 [iztro](https://github.com/SylarLong/iztro) 逐字段完全一致**。 iztro 是开源社区里最完整、维护时间最长的紫微斗数排盘实现之一, 不少前端项目在用。x-iztro 把它当作对照基准, 用约 71 万个测试用例逐字段核对 —— 任何一处不一致都当作 bug 修掉。 这句话的**边界**也要说清楚:与 iztro 一致不等于「命理界唯一正解」。 斗数流派众多,不同流派的安星与四化取法本来就不同。 x-iztro 保证的是「在同一套流派取舍下,算得和权威实现一模一样」, 而不是「这套流派取舍就是对的」。 详见[准确性保证](/zh/docs/guide/about/accuracy)。 ## MIT 是什么意思 [#mit-是什么意思] x-iztro 用 MIT 许可证开源。对使用方来说这意味着: * **可以商用**,不需要付费,也不需要跟作者报备 * **可以闭源使用**:把它装进你们的商业产品里,产品本身不必开源 * **可以修改** * 唯一的义务是**保留版权声明**(通常放在产品的「开源许可」页面里) * 作者**不承担任何担保责任**:用出问题是你们自己的事 在开源许可证里,MIT 是限制最少的那一类,法务通常不会有意见。 # AI Prompt 生成 (/zh/docs/guide/guides/ai-prompt) 把一张盘或一段运限转成结构化文本,直接交给大模型分析。 *适合:所有人。这是这个库最直接的用法* 要让大模型解读命盘,得先把盘描述给它。手工拼接这段描述既繁琐又容易漏字段, 所以 x-iztro 内置了两个生成函数。 | 函数 | 内容 | | --------------------- | ------------------------------------ | | `astrolabe_to_prompt` | 本命盘:基本信息 + 十二宫的干支、大限、小限虚岁、四组十二神、三组星耀 | | `horoscope_to_prompt` | 运限:大限、小限、流年、流月、流日、流时及各自四化与流耀 | 两者都按**盘面语言**生成:中文盘出中文 prompt,英文盘出英文 prompt。 ## 用法 [#用法] ```python natal = astro.astrolabe_to_prompt(chart) fortune = astro.horoscope_to_prompt(chart, "2025-1-1", 0) prompt = f"{natal}\n{fortune}" ``` ```rust let natal = astrolabe_to_prompt(&astrolabe, lang); let fortune = horoscope_to_prompt(&astrolabe, &horoscope, lang); ``` ```go natal, _ := chart.AstrolabeToPrompt() fortune, _ := chart.HoroscopeToPrompt("2025-1-1", 0) ``` Rust 侧的 `horoscope_to_prompt` 接受已算好的运限对象; Python 与 Go 侧接受目标日期,内部完成运限计算。三者输出逐字一致。 ## 本命盘 prompt 长什么样 [#本命盘-prompt-长什么样] 下面是 2000-8-16 寅时 女这张盘的完整开头与前三宫: ```text === 基本信息 === 性别: 女 阳历: 2000-8-16 农历: 二〇〇〇年七月十七 干支: 庚辰 甲申 丙午 庚寅 时辰: 寅时 (03:00~05:00) 星座: 狮子座 生肖: 龙 命宫地支: 午 身宫地支: 戌 命主: 破军 身主: 文昌 五行局: 木三局 生年四化: 太阳禄, 武曲权, 太阴科, 天同忌 === 十二宫 === --- 财帛 --- 天干地支: 戊寅 大限: 43-52 小限虚岁: 9, 21, 33, 45, 57, 69, 81, 93, 105, 117 十二神: 绝, 飞廉, 吊客, 岁驿 主星: 武曲(得)[权], 天相(庙) 辅星: 天马 杂耀: 解神, 三台, 天寿, 天巫, 天厨, 阴煞, 天哭 --- 夫妻 [来因] --- 天干地支: 庚辰 大限: 23-32 小限虚岁: 7, 19, 31, 43, 55, 67, 79, 91, 103, 115 十二神: 死, 将军, 岁建, 华盖 主星: 七杀(庙) 辅星: 右弼, 火星(陷) 杂耀: 封诰, 华盖 --- 官禄 [身宫] --- 天干地支: 丙戌 大限: 83-92 小限虚岁: 1, 13, 25, 37, 49, 61, 73, 85, 97, 109 十二神: 沐浴, 伏兵, 大耗, 月煞 主星: 廉贞(利), 天府(庙) 辅星: 左辅 杂耀: 天才, 天虚 ``` 十二宫按盘上位置顺序输出,不是按宫名顺序。 ## 运限 prompt 长什么样 [#运限-prompt-长什么样] 目标日期 2025-1-1 早子时: ```text === 运限 === 目标日期: 2025-1-1 / 二〇二四年腊月初二 --- 大限 --- 大限命宫: 本命夫妻 (庚辰) 大限四化: 太阳, 武曲, 太阴, 天同 夫妻 (财帛): 主星: 武曲(得)[权], 天相(庙) 辅星: 天马 流耀: 运马 兄弟 (子女): 主星: 太阳(庙)[禄], 天梁(庙) 流耀: 运曲 ... 小限命宫: 本命官禄 (虚岁 25) 主星: 廉贞(利), 天府(庙) 辅星: 左辅 杂耀: 天才, 天虚 --- 流年 --- 流年命宫: 本命夫妻 (甲辰) 流年四化: 廉贞, 破军, 武曲, 太阳 夫妻 (财帛): 主星: 武曲(得)[权], 天相(庙) 辅星: 天马 流耀: 流禄, 流马 十二神: 吊客, 岁驿 ... 流月命宫: 本命仆役 (丁丑) 流月宫名: 田宅, 官禄, 仆役, 迁移, 疾厄, 财帛, 子女, 夫妻, 兄弟, 命宫, 父母, 福德 流月四化: 太阴, 天同, 天机, 巨门 流日命宫: 本命迁移 (庚午) 流日宫名: 福德, 田宅, 官禄, 仆役, 迁移, 疾厄, 财帛, 子女, 夫妻, 兄弟, 命宫, 父母 流日四化: 太阳, 武曲, 太阴, 天同 流时命宫: 本命迁移 (丙子) 流时宫名: 福德, 田宅, 官禄, 仆役, 迁移, 疾厄, 财帛, 子女, 夫妻, 兄弟, 命宫, 父母 流时四化: 天同, 天机, 文昌, 廉贞 ``` ## 格式约定 [#格式约定] | 记号 | 含义 | | -------------------- | -------------------------------------------------- | | `天同(利)` | 括号内是[亮度](/zh/docs/guide/concepts/stars#亮度) | | `太阳(旺)[权]` | 方括号内是[四化](/zh/docs/guide/concepts/mutagen) | | `--- 官禄 [身宫] ---` | 方括号标记该宫同时是身宫 | | `--- 夫妻 [来因] ---` | 方括号标记该宫是[来因宫](/zh/docs/guide/concepts/palaces#来因宫) | | `大限命宫: 本命夫妻 (庚辰)` | 这一层的命宫落在本命的哪个宫,括号内是该层干支 | | `小限命宫: 本命官禄 (虚岁 25)` | 小限落宫,括号内是虚岁 | | `夫妻 (财帛):` | 运限段中,**前面是这一层重排后的宫名,括号内是本命宫名** | | `流月宫名: …` | 该层重排后的十二宫名,按盘上位置排列 | `夫妻 (财帛):` 说的是「这一格在本大限里叫夫妻宫,它在本命盘上是财帛宫」。 运限解读要以前面那个名字为准,括号里的名字是为了让你能对回本命盘。 见[运限](/zh/docs/guide/concepts/horoscope#同一个格子宫名会变)。 日期字段原样回显入参,不补零:传 `"2000-8-16"` 输出就是 `阳历: 2000-8-16`。 ## 篇幅 [#篇幅] 中文盘的本命 prompt 约 1,780 字符,运限段约 1,770 字符,两段合起来不到 4,000 字符。 英文盘更长(星名是单词而非两字),本命约 3,470、运限约 3,860 字符。 任何主流模型的上下文窗口都装得下,通常不必裁剪。 ## 接入大模型 [#接入大模型] 生成的文本是纯描述,不含指令。实际使用时在前面加上你的分析要求: ```python system = "你是紫微斗数分析师。基于给定命盘作答,不要编造盘上没有的信息。" user = f"""{astro.astrolabe_to_prompt(chart)} {astro.horoscope_to_prompt(chart, "2025-1-1", 0)} 请分析这个人 2025 年的事业运势。""" ``` 更完整的接法(工具调用、别让模型自己排盘)见[让 AI 解读命盘](/zh/docs/guide/guides/llm)。 优先用中文盘。英文盘的星名走 iztro 的意译词表(紫微是 `emperor`、七杀是 `marshal`), 亮度退化成 `[+3]` 这类记号,四化写成 `A`/`B`/`C`/`D` —— 这套写法与英文命理界的通行译法不同,模型未必认得。 主流模型的中文命理术语能力都不差,直接喂中文 prompt 效果更好。 确实需要英文时,建议附上一份[星名对照表](/zh/docs/guide/concepts/stars#星名对照表)。 ## 需要更细的控制 [#需要更细的控制] 生成函数覆盖的是通用场景。如果要自定义 prompt 结构(比如只描述特定几个宫、 或者输出 JSON 而非文本),直接遍历星盘数据自己拼即可 —— 所有字段都是公开的,见[数据结构字典](/zh/docs/guide/data-model)。 # 让 AI 解读命盘 (/zh/docs/guide/guides/llm) 排盘交给库、解读交给模型:怎么把 x-iztro 接进 AI 应用,以及几条踩过的坑。 *适合:开发者 · 产品与决策者* 这是最重要的一条。大模型算不准干支与安星 —— 它会给出**看起来合理但错误**的结果, 而且错得毫无规律,你没法从输出上看出来。 排盘是确定性计算,交给库;模型只负责解读。 这条分工是把斗数接进 AI 应用的整个前提。 ## 最小接法 [#最小接法] 把盘转成文本,前面加上你的分析要求,一起发给模型: ```python from x_iztro import Astro astro = Astro() chart = astro.by_solar("2000-8-16", 2, "female") system = "你是紫微斗数分析师。基于给定命盘作答,不要编造盘上没有的信息。" user = f"""{astro.astrolabe_to_prompt(chart)} {astro.horoscope_to_prompt(chart, "2025-1-1", 0)} 请分析这个人 2025 年的事业运势。""" ``` 生成的文本长什么样、格式怎么读,见 [AI Prompt 生成](/zh/docs/guide/guides/ai-prompt)。 ## 做成工具调用 [#做成工具调用] 让模型自己决定什么时候排盘,比在应用里写死流程更灵活: 模型负责理解需求与解读,x-iztro 负责算准。一个最小的工具定义: ```python { "name": "cast_chart", "description": "紫微斗数排盘。给定阳历生日、时辰索引与性别,返回完整命盘的结构化描述。", "input_schema": { "type": "object", "properties": { "solar_date": {"type": "string", "description": "阳历生日,格式 YYYY-M-D"}, "time_index": {"type": "integer", "minimum": 0, "maximum": 12, "description": "时辰索引,0=早子时(00-01),12=晚子时(23-24)"}, "gender": {"type": "string", "enum": ["male", "female"]}, }, "required": ["solar_date", "time_index", "gender"], }, } ``` 实现里调 `astrolabe_to_prompt` 返回文本即可。 运限单独做一个工具(多收一个目标日期),让模型按需要取。 用户说的「晚上 11 点」对应索引 `12` 而不是 `0`,模型不会自己知道。 把 0–12 的含义写进参数描述,或者干脆让工具收「出生时间 HH:MM」再由你换算。 ## 用中文盘喂模型 [#用中文盘喂模型] 排盘结果本身与盘面语言无关,但生成的 prompt 会跟着变。默认的中文盘就是最好的选择: 主流模型的中文命理术语能力都不差;而英文盘的星名走 iztro 的意译词表 (紫微 `emperor`、七杀 `marshal`),亮度退化成 `[+3]` 这类记号,四化写成 `A`/`B`/`C`/`D`, 这套写法与英文命理界的通行译法不同,模型未必认得。 确实需要英文输出时,做法是**用中文盘喂模型、让模型用英文作答**, 而不是换成英文盘。 ## 判断逻辑用标识 [#判断逻辑用标识] 如果你的应用要基于盘的内容做分支(例如「命宫有化忌时走另一套话术」), 用[语言无关标识](/zh/docs/guide/guides/keys)判断,不要匹配文本 —— 否则换一种盘面语言,所有分支都会静默失效。 ```python soul = chart.palace("soulPalace") if soul.has_mutagen("sihuaJi"): prompt_style = "谨慎" ``` ## 别把模型的解读当计算结果 [#别把模型的解读当计算结果] 模型可能在解读里顺手「补」一些盘上没有的信息 —— 多出一颗星、把大限区间说错、把宫名记混。 如果解读要落进产品(写库、发推送、做决策), 凡是可以从盘上直接取的事实,都从盘上取,不要从模型的自然语言里回抽。 模型输出只当文字用。 ## 让 AI 读这份文档 [#让-ai-读这份文档] 本站另有专供模型抓取的纯文本端点(`llms.txt`、单页 Markdown), 见[给 AI 读的文档端点](/zh/docs/guide/guides/llms-txt)。 # Config 详解 (/zh/docs/guide/guides/config) 六个开关分别改变什么、自定义四化表与亮度表怎么传、什么时候会看出差别。 *适合:开发者 · 命理爱好者(前半页的流派差异不需要会写代码)* 排盘中真正有分歧的地方都收在 `Config` 里:六个开关,外加两张可整表替换的数据表。 默认值与 JS iztro 完全一致,所以**不传配置就能得到与 iztro 相同的盘**。 | 开关 | 取值 | 默认 | 管什么 | | ------------------ | ---------------------------- | --------- | -------------- | | `year_divide` | `normal` / `exact` | `normal` | 排盘年干支按哪天换年 | | `horoscope_divide` | `normal` / `exact` | `normal` | 运限干支与月柱按哪天分界 | | `age_divide` | `normal` / `birthday` | `normal` | 虚岁什么时候加一 | | `day_divide` | `forward` / `current` | `forward` | 晚子时算今天还是明天 | | `algorithm` | `default` / `zhongzhou` | `default` | 算法派别 | | `astro_type` | `heaven` / `earth` / `human` | `heaven` | 排盘视角(天盘/地盘/人盘) | 另有两张覆盖表:`mutagens`(自定义四化表)与 `brightness`(自定义亮度表), 见[自定义四化表与亮度表](#自定义四化表与亮度表)。 ## 怎么传 [#怎么传] ```rust use x_iztro::data::types::*; let config = Config { algorithm: Algorithm::Zhongzhou, year_divide: YearDivide::Exact, ..Config::default() }; by_solar("2000-8-16", 2, Gender::Female, true, Language::ZhCN, config)?; ``` ```python from x_iztro import ChartConfig from x_iztro.enums import Algorithm, YearDivide config = ChartConfig( algorithm=Algorithm.ZHONGZHOU, year_divide=YearDivide.EXACT, ) astro.by_solar("2000-8-16", 2, "female", config=config) ``` ```go cfg := &iztro.Config{ Algorithm: "zhongzhou", YearDivide: "exact", } iztro.BySolar("2000-8-16", 2, iztro.GenderFemale, true, iztro.LanguageZhCN, cfg) ``` Python 与 Go 都只需填要改的键,其余取默认;Rust 用 `..Config::default()` 达到同样效果。 *** ## 年分界点 `year_divide` [#年分界点-year_divide] 决定**排盘用的年干支**在哪一天换年。 | 取值 | 换年时点 | | ------------ | ------ | | `normal`(默认) | 农历正月初一 | | `exact` | 立春 | 年干支是一连串东西的源头:本命四化、命主与身主、十二宫宫干。 所以这个开关一旦改变,整张盘可能大幅不同。 **什么时候会看出差别**:出生在农历正月初一与立春之间的人。 这两个日期通常相差几天到半个月,落在这个窗口里的生日,两种配置排出的年干支相差一位。 八字体系一律以立春换年,所以要与八字盘对齐时选 `exact`。 紫微斗数的通行做法是以正月初一换年,`normal` 也是 iztro 的默认。 拿不准就别动 —— 改了就不再与 iztro 的默认输出一致。 x-iztro 复刻了 iztro 内部的一处细节:依赖年支的东西并非全走同一个开关。 分工是固定的三条: 1. **跟 `year_divide` 的年干支**:生年四化、命主与身主、十二宫宫干、 禄存、擎羊、陀罗、天魁、天钺、天马、红鸾、天喜、长生十二神、博士十二神 2. **跟 `horoscope_divide` 的年干支**:其余全部年系杂耀, 以及本命盘上的岁前十二神与将前十二神 3. **跟 `horoscope_divide` 的月分界**:本命四柱里的**月柱** 两个开关可以分别设,所以「主星按一个年支、部分杂耀按另一个年支」是可能出现的。 这看起来不对称,但它就是 iztro 的实际行为,为保证零差异必须原样保留。 ## 运限分界点 `horoscope_divide` [#运限分界点-horoscope_divide] 决定**运限干支**、**本命月柱**与**干支纪月**在哪一天分界。 | 取值 | 年分界 | 月分界 | | ------------ | ---- | ---------- | | `normal`(默认) | 正月初一 | 初一,以五虎遁推月干 | | `exact` | 立春 | 节气 | **什么时候会看出差别**:查询日期落在年初(正月初一到立春之间)或每个节气交接的前后, 流年与流月的干支会差一位,进而改变运限四化。 这个开关还会改**本命盘的月柱**——不只是运限。 例如 2000-8-5 寅时:`normal` 下四柱是 `庚辰 甲申 乙未 戊寅`, `exact` 下是 `庚辰 癸未 乙未 戊寅`,月柱由甲申变癸未。 ## 虚岁分界点 `age_divide` [#虚岁分界点-age_divide] 决定**虚岁**什么时候加一,直接影响小限落在哪个宫。 | 取值 | 加岁时点 | | ------------ | ---------- | | `normal`(默认) | 跨农历年即加一岁 | | `birthday` | 过了农历生日才加一岁 | **什么时候会看出差别**:查询日期落在农历新年与本人农历生日之间。 这段时间两种配置的虚岁相差一岁,小限也就落在相邻的两个宫。 ## 晚子时归属 `day_divide` [#晚子时归属-day_divide] 决定 23:00–24:00 出生(时辰索引 `12`)的人,日柱按哪一天算。 | 取值 | 行为 | | ------------- | ------------------- | | `forward`(默认) | 晚子时归**次日**,按次日的日柱排盘 | | `current` | 晚子时归**当天**,按当日早子时排盘 | **什么时候会看出差别**:只影响时辰索引为 `12` 的盘,其余时辰完全无差异。 `forward` 会把日柱与**起紫微用的农历日**一起推到次日, 但星盘上的农历日期展示串仍显示**出生当日**。 例如 2000-8-16 晚子时:农历日期照旧显示「二〇〇〇年七月十七」, 四柱却是 `庚辰 甲申 丁未 庚子` —— 日柱丁未已是 8 月 17 日的。 读结果时别拿农历展示串去反推日柱。 无论选哪种,时辰索引字段都保留原始传入值 `12`, 不会因为归到次日就变成 `0` —— 这样调用方始终能知道出生的真实时辰。 ## 算法派别 `algorithm` [#算法派别-algorithm] | 取值 | 说明 | | ------------- | ----------------------- | | `default`(默认) | 通行的安星规则,与 JS iztro 默认一致 | | `zhongzhou` | 中州派 | 中州派与默认派的差别集中在四处,**四化表不在其中**: | 改动 | `default` | `zhongzhou` | | ----- | ------------------------ | -------------------------------------------------- | | 命主怎么查 | 按**命宫地支** | 按**生年地支**(因此换命宫重排时命主不再变) | | 岁前十二神 | 大耗 `dahao` | 岁破 `suipo` | | 杂耀 | 截路 `jielu`、空亡 `kongwang` | 截空 `jiekong`、劫杀 `jieshaAdj`、大耗 `dahao`、龙德 `longde` | | 天伤天使 | 天伤在仆役、天使在疾厄 | 阴阳男女互换,两星位置对调 | 庚干化科在两派下都是太阴 —— 要换四化取法请用下面的自定义四化表, 不要指望 `algorithm`。 ```python astro.by_solar("1990-11-5", 4, "male", config=ChartConfig(algorithm=Algorithm.ZHONGZHOU)) ``` ## 排盘视角 `astro_type` [#排盘视角-astro_type] 中州派把同一组出生数据看作三张盘,差别只在**用哪一宫的干支起五行局**: | 视角 | 起五行局的宫 | 新盘的命宫 | | ----------- | ------ | ----------- | | `heaven` 天盘 | 命宫 | 命宫(即常规排盘结果) | | `earth` 地盘 | 身宫 | 身宫 | | `human` 人盘 | 福德宫 | 福德宫 | 五行局一变,紫微天府的落点、十二宫名、身宫地支、长生十二神、大限小限全部跟着变; 辅星、杂耀(天伤天使天才随命宫走,会重新落位)、博士十二神、 岁前将前十二神沿用天盘。 ```python earth = astro.by_solar("2000-8-16", 2, "female", config=ChartConfig(astro_type=AstroType.EARTH)) ``` ```go earth, _ := iztro.BySolar("2000-8-16", 2, iztro.GenderFemale, true, iztro.LanguageZhCN, &iztro.Config{AstroType: iztro.AstroEarth}) ``` ```rust let earth = by_solar("2000-8-16", 2, Gender::Female, true, Language::ZhCN, Config::default().with_astro_type(AstroType::Earth))?; ``` JS iztro 把 `astroType` 放在 `withOptions` 的选项对象上,因为它的 `config()` 是全局单例、装不下按盘变化的值。x-iztro 的配置本来就随盘传入, 所以直接收进 `Config`,两个排盘入口都能用。 ### 从任意干支起盘 [#从任意干支起盘] 天盘、地盘、人盘之外,也可以指定任意干支为命宫重排: ```python body = chart.palace(PalaceName.BODY) earth = chart.rearranged(body.heavenly_stem_key, body.earthly_branch_key) ``` ```go earth, _ := chart.Rearranged(body.HeavenlyStemKey, body.EarthlyBranchKey) ``` ```rust let earth = chart.rearranged(body.heavenly_stem, body.earthly_branch); ``` 以身宫干支重排,结果与 `astro_type = earth` 一致。 ## 自定义四化表与亮度表 [#自定义四化表与亮度表] 四化与亮度是流派分歧最集中的两处。`Config` 允许**按标识整表替换**: 给出某个天干的四化就只改那个天干,未给出的天干仍用默认表;亮度同理。 ### 四化表 [#四化表] 一个天干配四颗星,顺序固定为**禄、权、科、忌**,必须给满四项。 ```rust let config = Config::default().with_mutagens( HeavenlyStem::Geng, [StarKey::TaiyangMaj, StarKey::WuquMaj, StarKey::TiantongMaj, StarKey::TianfuMaj], ); by_solar("2000-8-16", 2, Gender::Female, true, Language::ZhCN, config)?; ``` ```python cfg = ChartConfig(mutagens={ "gengHeavenly": ["taiyangMaj", "wuquMaj", "tiantongMaj", "tianfuMaj"], }) chart = astro.by_solar("2000-8-16", 2, "female", config=cfg) ``` ```go cfg := &iztro.Config{Mutagens: map[string][]string{ "gengHeavenly": {"taiyangMaj", "wuquMaj", "tiantongMaj", "tianfuMaj"}, }} ``` 把庚干换成「天同化科、天府化忌」之后,这张庚年盘的四化变成: ```text 财帛 武曲 权 子女 太阳 禄 官禄 天府 忌 疾厄 天同 科 ``` 自定义四化表同时改变**全部飞星判断** —— 宫干化出哪四颗星走的是同一张表。 ### 亮度表 [#亮度表] 一颗星配十二个亮度,按盘上位置排列(第一项是寅宫),必须给满十二项; 该位置无亮度时用空值(Python / Go 传空串)。 ```python cfg = ChartConfig(brightness={ "ziweiMaj": ["miao", "wang", "de", "li", "ping", "bu", "xian", "miao", "wang", "de", "li", "ping"], }) ``` 1. **只收标识,不收译名**:`"ziweiMaj"` 可以,`"紫微"` 不行。 2. **长度严格校验**:四化表必须 4 项、亮度表必须 12 项,多一项少一项都会报错。 3. **不回显在输出里**:覆盖表是排盘的输入,不属于排盘结果, 星盘上回显的 `config` 只有六个开关,两张表读回来是空的。 自己要留档就自己存那份配置。 ## 配置会跟着星盘走 [#配置会跟着星盘走] 排盘用的配置存在星盘上,运限从那里取, 所以**运限一定与排盘用同一套配置**,不会出现本命盘用中州派、运限用默认派的错配。 ```python chart = astro.by_solar("2000-8-16", 2, "female", config=ChartConfig(age_divide="birthday")) h = chart.horoscope("2024-10-1", 0) # 自动沿用 age_divide=birthday ``` ```go chart, _ := iztro.BySolar("2000-8-16", 2, iztro.GenderFemale, true, iztro.LanguageZhCN, &iztro.Config{AgeDivide: "birthday"}) h, _ := chart.Horoscope("2024-10-1", 0) // 同上 ``` ## 测试覆盖 [#测试覆盖] 四个分界开关的非默认取值有 9,696 例专门的金标测试(含排盘层与运限层的组合), 中州派盘型另有 12,488 例,覆盖立春窗口逐日、晚子时、生日前后等所有会产生分歧的边界。 自定义四化表与亮度表另有一组专门的测试。见[准确性保证](/zh/docs/guide/about/accuracy)。 # 语言无关的 key 契约 (/zh/docs/guide/guides/keys) 为什么星名不能拿来做判断,key 字段是什么,三种编程语言各自怎么用。 *适合:开发者* ## 问题 [#问题] 排盘结果的文本会跟着盘面语言变。同一颗星,中文盘上是「紫微」,英文盘上是 `emperor`, 韩文盘上是 `자미`。如果判断逻辑写成: ```python # 反面例子 if any(s.name == "紫微" for s in soul.major_stars): ... ``` 那么这段代码只在 `language="zh-CN"` 时正确。换成任何其他盘面语言就静默失效 —— 不会报错,只是永远返回 `False`。这类 bug 很难发现。 ## 解法 [#解法] 每个会被翻译的字段,x-iztro 都额外提供一个**语言无关标识**(key)。 key 的取值是 iztro 的 i18n 键名,与盘面语言无关,永远不变。 ```json { "name": "紫微", "key": "ziweiMaj", "brightness": "庙", "brightnessKey": "miao", "mutagen": "禄", "mutagenKey": "sihuaLu" } ``` `name` 给人看,`key` 给代码用。 ## 有哪些 key 字段 [#有哪些-key-字段] | 数据 | 翻译字段 | 标识字段 | 取值示例 | | ------- | --------------------- | ---------------------- | ------------------- | | 星耀 | `name` | `key` | `ziweiMaj` | | 亮度 | `brightness` | `brightnessKey` | `miao` | | 四化 | `mutagen` | `mutagenKey` | `sihuaLu` | | 宫位名 | `name` | `nameKey` | `soulPalace` | | 天干 | `heavenly_stem` | `heavenlyStemKey` | `jiaHeavenly` | | 地支 | `earthly_branch` | `earthlyBranchKey` | `ziEarthly` | | 五行局 | `five_elements_class` | `fiveElementsClassKey` | `water2nd` | | 命主 / 身主 | `soul` / `body` | `soulKey` / `bodyKey` | `ziweiMaj` | | 性别 | `gender` | `genderKey` | `male` | | 长生十二神 | `changsheng12` | `changsheng12Key` | `changsheng` | | 博士十二神 | `boshi12` | `boshi12Key` | `boshi` | | 将前十二神 | `jiangqian12` | `jiangqian12Key` | `jiangxing` | | 岁前十二神 | `suiqian12` | `suiqian12Key` | `suijian` | | 宫干四化星 | — | `mutagenStarKeys` | `["taiyangMaj", …]` | ## 三种编程语言的用法 [#三种编程语言的用法] ### Python:枚举 [#python枚举] `x_iztro.enums` 里所有枚举都是 `StrEnum`,**成员的值就是 key**。 ```python from x_iztro.enums import MajorStar, Mutagen, PalaceName, Brightness MajorStar.ZIWEI # "ziweiMaj" Mutagen.LU # "sihuaLu" PalaceName.SOUL # "soulPalace" Brightness.MIAO # "miao" ``` 判断方法接受枚举: ```python soul = chart.palace(PalaceName.SOUL) soul.has([MajorStar.ZIWEI]) soul.has_mutagen(Mutagen.LU) ``` 因为是 `StrEnum`,它同时也是字符串,可以直接和 key 字段比较: ```python star.key == MajorStar.ZIWEI # True ``` ### Go:常量 [#go常量] `keys.go` 里的常量值就是 key: ```go iztro.PalaceSoul // "soulPalace" iztro.StarZiweiMaj // "ziweiMaj" iztro.MutagenLu // "sihuaLu" iztro.BrightnessMiao // "miao" soul := chart.Palace(iztro.PalaceSoul) soul.Has(iztro.StarZiweiMaj) star.WithMutagen(iztro.MutagenQuan) star.WithBrightness(iztro.BrightnessMiao) ``` ### Rust:枚举本身 [#rust枚举本身] Rust 侧不需要 key 字段 —— 结构体里存的本来就是枚举,翻译是显示时才做的事。 ```rust if soul.has(&[StarKey::ZiweiMaj]) { } ``` 需要 key 字符串时(例如自己做序列化)调 `as_key()`: ```rust Palace::Soul.as_key(); // "soulPalace" Mutagen::Lu.as_key(); // "sihuaLu" Brightness::Miao.as_key(); // "miao" ``` ## 验证方式 [#验证方式] 同一个生日分别用六种盘面语言排盘,所有 key 字段必须逐一相等 —— 这条由绑定契约测试(`golden_contract`)与 Go / Python 的端到端金标测试守着。 所以下面这段代码在任何盘面语言下结果都相同: ```python for lang in ["zh-CN", "zh-TW", "en-US", "ja-JP", "ko-KR", "vi-VN"]: chart = astro.by_solar("2000-8-16", 2, "female", language=lang) soul = chart.palace(PalaceName.SOUL) assert soul.has_mutagen(Mutagen.LU) == expected ``` ## 什么时候可以用文本 [#什么时候可以用文本] 展示。只有展示。任何进入 `if` 的比较都应该用 key。 # 多语言输出 (/zh/docs/guide/guides/i18n) 六种盘面语言、哪些字段会被翻译、换盘面语言对结果的影响,以及标识与译名的双向换算。 *适合:开发者* ## 支持的盘面语言 [#支持的盘面语言] 「盘面语言」指排盘结果里那些人读的文本用哪种语言写,与你用哪种**编程语言**调用无关。 | 取值 | 语言 | Rust 枚举 | | ------- | -------- | ---------------- | | `zh-CN` | 简体中文(默认) | `Language::ZhCN` | | `zh-TW` | 繁体中文 | `Language::ZhTW` | | `en-US` | 英文 | `Language::EnUS` | | `ja-JP` | 日文 | `Language::JaJP` | | `ko-KR` | 韩文 | `Language::KoKR` | | `vi-VN` | 越南文 | `Language::ViVN` | ```python chart = astro.by_solar("2000-8-16", 2, "female", language="en-US") ``` ```go chart, _ := iztro.BySolar("2000-8-16", 2, iztro.GenderFemale, true, iztro.LanguageJaJP, nil) ``` ```rust by_solar("2000-8-16", 2, Gender::Female, true, Language::KoKR, Config::default())?; ``` ## 哪些内容会翻译 [#哪些内容会翻译] 会翻译的是所有面向人阅读的文本: * 星耀名、宫位名、四化名、亮度名 * 天干、地支、五行局 * 时辰名与时间段、星座、生肖、性别 * 农历日期的中文表示、干支纪日展示串 * 运限层级名(大限 / 流年 / …) **不翻译**的是所有标识字段与数值字段:星耀的 `key`、宫位的 `nameKey`、 宫位索引、大限区间、虚岁等。见 [key 契约](/zh/docs/guide/guides/keys)。 1. **英文与韩文没有亮度译名**,输出的是记号:`[+3]`(庙)、`[+2]`(旺)、 `[+1]`(得)、`[0]`(利)、`[-1]`(平)、`[-2]`(不)、`[-3]`(陷)。 繁体、日文、越南文有真译名。 2. **英文四化输出 `A`/`B`/`C`/`D`**,依次是禄、权、科、忌。 3. **非中文译名沿用 iztro 的词表,不保证是该语言命理界的通行译法**, 个别条目还是误译 —— 例如韩文把「来因宫」译成 `라인`(line 的音译)。 译名只做展示,判断一律用标识字段。 ## 换盘面语言不改变排盘结果 [#换盘面语言不改变排盘结果] 盘面语言只影响翻译层。同一个生日在六种盘面语言下: * 十二宫的位置与宫名顺序完全相同 * 每个宫里的星耀完全相同 * 四化、亮度、大限小限、运限干支完全相同 变的只是这些东西被写成什么字。所以下面两张盘除文本外逐字段相等: ```python zh = astro.by_solar("2000-8-16", 2, "female", language="zh-CN") en = astro.by_solar("2000-8-16", 2, "female", language="en-US") assert zh.palace(PalaceName.SOUL).index == en.palace(PalaceName.SOUL).index assert zh.soul_key == en.soul_key ``` 六种盘面语言的一致性由变体金标测试覆盖,零容忍差异。 ## 标识与译名的换算 [#标识与译名的换算] 手上只有标识(或只有某种语言的译名)时,用双向查找函数换算,不必重新排盘: ```rust translate_key("ziweiMaj", Language::EnUS); // Some("emperor") key_of("emperor"); // Some("ziweiMaj") key_of("자미"); // Some("ziweiMaj") ``` 类别已知时用强类型版本更直接,也免去 `Option`: ```rust use x_iztro::{translate_palace, translate_star}; translate_star(StarKey::ZiweiMaj, Language::ViVN); // Tử Vi translate_palace(Palace::Soul, Language::KoKR); // 명궁 ``` ```python i18n.translate("ziweiMaj", "en-US") # emperor i18n.key_of("emperor") # ziweiMaj i18n.key_of("자미") # ziweiMaj ``` ```go name, _ := iztro.Translate(iztro.StarZiweiMaj, iztro.LanguageEnUS) // emperor key, _ := iztro.KeyOf("emperor") // ziweiMaj key, _ = iztro.KeyOf("자미") // ziweiMaj ``` Go 侧两个函数都返回 `(string, error)`:标识未知或反查不到时返回空串与 `*iztro.Error`(分类 `invalid_argument`)。 覆盖十二类共 260 个标识:星耀、宫位(含身宫、来因宫)、天干、地支、亮度、四化、 五行局、性别、生肖、时辰、星座、运限层级。完整清单与逐条说明见 [Rust](/zh/docs/rust/i18n)、[Python](/zh/docs/python/i18n)、[Go](/zh/docs/go/i18n) 三页。 `key_of("不存在的名字")` 返回 `None` / 空串,而不是把入参吐回来。 另有同形译名的问题:不同标识在某些语言下译名相同(`horse`、`dragon`、`유시` 等)。 反查按固定的扫描顺序取第一个命中,与 iztro 的 `kot` 逐例一致。 要指定类别就用 `key_of_in`(Rust)/ `key_of(text, key_filter)`(Python)/ `KeyOfIn`(Go),传标识名的共同后缀消歧: `"Maj"` 只看十四主星、`"Min"` 只看辅星、`"Palace"` 只看宫位、`"Hour"` 只看时辰。 ## 三种编程语言的取值形态不同 [#三种编程语言的取值形态不同] | | 排盘结果里存什么 | 换盘面语言的代价 | | ------ | ------------------------------------------ | ------------------- | | Rust | 枚举(`StarKey`、`Palace`…),只有少数展示字段是 `String` | 调翻译函数,同一张盘可同时输出多种语言 | | Python | 译名与标识两组字段都已经是字符串 | 重新排盘 | | Go | 同上 | 重新排盘 | 排盘本身是毫秒级,多排几次不是问题。判断逻辑请始终用标识字段, 这样换盘面语言不需要改任何代码。 ## 没有全局语言开关 [#没有全局语言开关] x-iztro 不设「当前语言」这样的全局状态:排盘时语言随参数传入, 翻译函数每次调用都显式指定目标语言。 全局语言开关会让同一段代码在不同调用顺序下产出不同结果,并发环境尤其危险。 显式传参使每次调用的结果只由入参决定。 ## 新增一种语言要改哪些地方 [#新增一种语言要改哪些地方] 词表不是一个可以外挂的资源文件,是编译进库的静态表。加一种语言要动四处: `src/data/types.rs` 的 `Language` 枚举加一个变体,并在 `as_code` / `from_code` 里补上语言代码 `src/i18n/` 下新增一个词表文件,实现与既有文件相同的一组函数(星名、宫名、干支名、亮度、四化……) `src/i18n/mod.rs` 里每个翻译函数的 `match` 各加一条分派 —— 这里是逐个函数的,不是一处 `src/i18n/lookup.rs` 的 `lang_index` 与反查扫描顺序表加一项;反查顺序会影响同形译名落到哪个标识,须与金标对照 绑定层不需要改:语言代码是字符串传入的,加了枚举变体三侧自动可用。 # 错误处理 (/zh/docs/guide/guides/errors) 校验了什么、在哪一层校验、四个错误分类各是什么,以及为什么核心层坚持不 panic。 *适合:开发者* x-iztro 把外部输入的校验放在尽量靠内的一层,三种编程语言共用同一道防线。 各语言只把错误翻译成自己的惯例类型,不重复校验、也不各自解释。 ## 错误分类 [#错误分类] 每个错误都带一个机器可读的分类,跨语言取值相同 —— **判断用它,不要解析文案**。 | 分类 | 含义 | | -------------------- | ------------------------------------- | | `invalid_date` | 日期格式非法、该日期不存在,或超出支持范围(公历 1583–9999) | | `invalid_time_index` | 时辰索引越界(合法值 0–12) | | `invalid_argument` | 其余入参或配置非法:未知的性别、盘面语言、星耀标识、开关取值、覆盖表长度错 | | `internal` | 库内部缺陷或运行时故障,不是调用方的错,请上报 | ## 各语言的错误类型 [#各语言的错误类型] `IztroError` 枚举,`code()` 给出分类: ```rust match by_solar(date, ti, Gender::Female, true, Language::ZhCN, Config::default()) { Ok(chart) => { /* ... */ } Err(e) => println!("{:20} {}", e.code(), e), } ``` ```text invalid_date invalid solar date '2000-2-30': day is out of range for that month invalid_date invalid solar date '1000-1-1': year must be within 1583-9999 invalid_time_index time_index must be 0-12, got 13 ``` 变体有三个:`InvalidDate`、`InvalidTimeIndex`、`Internal`。 枚举标了 `#[non_exhaustive]`,`match` 时请留 `_` 分支。 `IztroError`,继承 `ValueError`,所以既有的 `except ValueError` 依然能捕获: ```python from x_iztro import Astro, IztroError try: Astro().by_solar("2000-2-30", 2, "female") except IztroError as e: print(e.code, e) ``` ```text invalid_date invalid solar date '2000-2-30': day is out of range for that month ``` `*iztro.Error`,带 `Code` 与 `Message`;四个哨兵变量配合 `errors.Is` 按类别匹配: ```go _, err := iztro.BySolar("2000-13-1", 2, iztro.GenderMale, true, iztro.LanguageZhCN, nil) if errors.Is(err, iztro.ErrInvalidDate) { var e *iztro.Error errors.As(err, &e) fmt.Println(e.Code, e.Message) } ``` ```text invalid_date invalid solar date '2000-13-1': month must be within 1-12 ``` | 哨兵 | 对应分类 | | --------------------- | ---------------------- | | `ErrInvalidDate` | `CodeInvalidDate` | | `ErrInvalidTimeIndex` | `CodeInvalidTimeIndex` | | `ErrInvalidArgument` | `CodeInvalidArgument` | | `ErrInternal` | `CodeInternal` | `Error()` 输出带 `iztro: ` 前缀;`Message` 是不带前缀的原文。 C FFI 与 wasm 出口把同一个错误落成 `{"error":"","code":""}`, 由 serde 生成以保证转义完备。 ## 校验范围与消息样例 [#校验范围与消息样例] 消息一律小写起首、以冒号引出细节,并带上原始输入 —— 批量处理时能直接定位是哪一条数据出的问题。 | 输入 | 消息样例 | | -------- | ------------------------------------------------------------------------------------ | | 公历日期格式 | `invalid solar date 'not-a-date': year is not a number` | | 公历日期不存在 | `invalid solar date '2000-2-30': day is out of range for that month` | | 公历年份范围 | `invalid solar date '1000-1-1': year must be within 1583-9999` | | 农历月份 | `invalid lunar date '2000-13-1': month must be within 1-12` | | 农历该月天数 | `invalid lunar date '2000-2-31': day is out of range for that lunar month` | | 时辰索引 | `time_index must be 0-12, got 13` | | 性别 | `invalid gender 'x': expected 'male' or 'female'` | | 盘面语言 | `invalid language 'fr-FR': expected one of zh-CN, zh-TW, en-US, ja-JP, ko-KR, vi-VN` | | 自定义四化表长度 | `invalid mutagens for 'gengHeavenly': expected 4 stars (lu, quan, ke, ji), got 3` | | 自定义表收到译名 | `invalid mutagens for 'gengHeavenly': unknown star '太阳'` | 日期与时辰在**核心层**校验,三种编程语言完全一致。 性别、盘面语言、配置开关、星耀标识这些以字符串传入的东西, 在**绑定层**解析时校验 —— Rust 侧它们本来就是枚举,不存在非法取值。 ## 查不到不是错误 [#查不到不是错误] 需要计算的入口返回错误;**查询**方法查不到时返回空值而非错误—— 「这张盘上没有这颗星」是正常结果,不是异常。 | 场景 | 返回 | | ---------- | -------------- | | 某颗星不在这张盘上 | `None` / `nil` | | 宫位索引越界 | `None` / `nil` | | 该星没有亮度表 | `None` / 空串 | | 反查一个不存在的译名 | `None` / 空串 | `chart.palace("soulPalce")` 不会报错,只会返回空;`has(["ziweiMj"])` 恒返回 `False`。 判断用枚举或常量(Python 的 `PalaceName.SOUL`、Go 的 `iztro.PalaceSoul`)—— 拼错时是编译期或构造期报错,不是运行期静默。 要校验一个外来的字符串,把它喂给枚举构造:`PalaceName("x")` 会抛 `ValueError`。 ## 为什么核心层不 panic [#为什么核心层不-panic] 这不是风格偏好,是 wasm 目标带来的硬约束。 wasm 上 panic 会变成 **trap** ,直接中止调用 `catch_unwind` 在 wasm 上 **无效** ——绑定层兜不住 每次 trap 都会 **永久损耗** 模块实例的栈空间,累积之后连合法调用都会失败 因此防线必须设在更靠内的一层:所有外部输入在进入算法前校验完毕,入口返回 `Result`。 绑定层的 `catch_unwind` 只负责兜底库内部的缺陷,不承担参数校验职责。 排盘入口不会因非法**外部输入**而 panic。若真的遇到,那是库内部缺陷, 会以 `internal` 分类返回,应作为 bug 上报——而不是调用方需要防御的情况。 ## 批量处理的写法 [#批量处理的写法] 坏数据跳过、好数据继续,而不是整批失败: ```rust let (charts, failed): (Vec<_>, Vec<_>) = rows .iter() .map(|r| by_solar(&r.date, r.ti, r.gender, true, Language::ZhCN, Config::default())) .partition(Result::is_ok); ``` ```python charts, failed = [], [] for row in rows: try: charts.append(Astro().by_solar(row["date"], row["ti"], row["gender"])) except IztroError as e: failed.append((row, e.code, str(e))) ``` ```go for _, row := range rows { chart, err := iztro.BySolar(row.Date, row.TimeIndex, row.Gender, true, iztro.LanguageZhCN, nil) if err != nil { var e *iztro.Error errors.As(err, &e) failed = append(failed, failure{row, e.Code, e.Message}) continue } charts = append(charts, chart) } ``` 逐条 API 的错误行为见 [Rust](/zh/docs/rust/errors)、[Python](/zh/docs/python/errors)、[Go](/zh/docs/go/errors) 三页。 # 扩展星盘 (/zh/docs/guide/guides/plugins) 把自己的分析规则挂到星盘上——三种编程语言各自的扩展点。 *适合:开发者* 排盘结果是数据,怎么解读是各家的事。斗数流派众多、判断规则千人千面, 把它们全塞进核心既不可能也不该做。x-iztro 的做法是让你把自己的规则 以方法的形式挂到星盘上,调用语法与内置方法一致。 ## 三种编程语言的扩展点 [#三种编程语言的扩展点] 每种编程语言用它自己最自然的机制,不强行统一成一套: | 编程语言 | 机制 | 检查时机 | 作用范围 | | -------------------------------- | -------- | ---- | ---------- | | [Rust](/zh/docs/rust/extend) | 扩展 trait | 编译期 | `use` 了才可见 | | [Python](/zh/docs/python/extend) | 往类上挂方法 | 运行期 | 进程内全局 | | [Go](/zh/docs/go/extend) | 结构体嵌入 | 编译期 | 只影响自己的类型 | 三者都做同一件事:`chart.my_method()` 这样调用,且能用上星盘的全部内置能力。 具体写法与可运行示例见各自的页面。 ## 共同的约定 [#共同的约定] `star.key == "ziweiMaj"` 在任何盘面语言下都成立; `star.name == "紫微"` 只在中文盘上成立。 扩展方法里做判断请用 `*_key` / `*Key` 字段或内置判断方法, 展示时才取译名。这样同一条规则在六种盘面语言的盘上结果一致。 详见[语言无关标识](/zh/docs/guide/guides/keys)。 `WealthAnalysis`、`CareerAnalysis`、`HealthAnalysis` 各自成一组, 使用方按需引入。堆成一个大集合会让所有调用点都被迫带上全部方法。 同一套规则要在三种编程语言上都可用时,当前的做法是三侧各写一遍, 再用一组断言同一张盘上同一组取值的测试守住。 因为判断基于语言无关标识,三份实现只要逻辑相同,结果必然相同—— 测试负责证明「逻辑确实相同」。 ## 一个例子 [#一个例子] 三种编程语言实现同一个插件:取命宫主星(空宫借对宫),并读出五行局的局数。 ```rust trait MyAnalysis { fn major_star(&self) -> String; } impl MyAnalysis for Astrolabe { fn major_star(&self) -> String { let soul = self.palace(Palace::Soul).expect("命宫必然存在"); let source = if soul.is_empty() { soul.opposite_palace() } else { soul }; source.major_stars.iter() .filter(|s| s.star_type == StarType::Major) .map(|s| translate_star(s.key, self.language)) .collect::>().join(",") } } chart.major_star() // 紫微 ``` ```python def my_analysis(cls: type[Astrolabe]) -> None: def major_star(self) -> str: soul = self.palace(PalaceName.SOUL) source = soul.opposite_palace() if soul.is_empty() else soul return ",".join(s.name for s in source.major_stars) cls.major_star = major_star load_plugin(my_analysis) chart.major_star() # 紫微 ``` ```go type MyChart struct{ *iztro.Astrolabe } func (c MyChart) MajorStar() string { soul := c.Palace(iztro.PalaceSoul) source := soul if soul.IsEmpty() { source = soul.OppositePalace() } names := []string{} for _, s := range source.MajorStars { if s.Type == iztro.StarTypeMajor { names = append(names, s.Name) } } return strings.Join(names, ",") } MyChart{chart}.MajorStar() // 紫微 ``` 三段代码在同一张盘上都返回 `紫微`,换成英文盘则都返回 `emperor`。 # 排盘九步对应的 API (/zh/docs/guide/guides/step-api) 排盘的九个步骤各自对应哪个公开函数,以及哪些步骤会被配置开关改变。 *适合:开发者* 排盘的[九个步骤](/zh/docs/guide/concepts/how-it-works)在 x-iztro 里**每一步都有对应的公开函数**。 日常排盘用不到它们——直接调排盘入口即可; 这一页面向两类需求:想核对某一步的推算,或想复用其中一段自建流程。 下面的示例用的是 2000-8-16 寅时 女这张盘,与 [排盘是怎么算的](/zh/docs/guide/concepts/how-it-works)一页同一个例子。 ## 步骤与 API 对照 [#步骤与-api-对照] ### 2. 定月索引 [#2-定月索引] ```python utils.fix_lunar_month_index(7, 17, False, 2, True) # 农历七月十七、非闰月、寅时、修正闰月 ``` ```text 6 ``` [Rust](/zh/docs/rust/util#fix_lunar_month_index--fix_lunar_day_index) · [Python](/zh/docs/python/util#fix_lunar_month_index--fix_lunar_day_index) · [Go](/zh/docs/go/util#fixlunarmonthindex--fixlunardayindex) ### 3. 定命宫身宫 [#3-定命宫身宫] ```python utils.get_soul_and_body(6, 2, "gengHeavenly") # 月索引、时辰索引、年干 ``` ```text SoulAndBody(soul_index=4, body_index=8, heavenly_stem_of_soul='renHeavenly', earthly_branch_of_soul='wuEarthly') ``` [Rust](/zh/docs/rust/util#get_soul_and_body) · [Python](/zh/docs/python/util#get_soul_and_body) · [Go](/zh/docs/go/util#getsoulandbody) ### 4. 定五行局 [#4-定五行局] ```python utils.get_five_elements_class("renHeavenly", "wuEarthly") # 命宫干、命宫支 ``` ```text wood3rd ``` [Rust](/zh/docs/rust/util#get_five_elements_class) · [Python](/zh/docs/python/util#get_five_elements_class) · [Go](/zh/docs/go/util#getfiveelementsclass) ### 5. 起紫微天府 [#5-起紫微天府] ```python star.get_start_index("2000-8-16", 2, "female") ``` ```text StartIndex(ziwei_index=4, tianfu_index=8) ``` [Rust](/zh/docs/rust/star#get_start_index) · [Python](/zh/docs/python/star#get_start_index) · [Go](/zh/docs/go/star#getstartindex) ### 6 与 7. 安主星、辅星与杂耀 [#6-与-7-安主星辅星与杂耀] 三组各有一个入口,返回十二宫的星耀分布; 另有各组的落宫索引函数(`get_lu_yang_tuo_ma_index`、`get_chang_qu_index` 等), 清单见各语言的安星模块页。 [Rust](/zh/docs/rust/star) · [Python](/zh/docs/python/star) · [Go](/zh/docs/go/star) ### 8. 安四组十二神 [#8-安四组十二神] `get_changsheng12`、`get_boshi12`、`get_yearly12`, 以及两个起点函数 `get_changsheng12_start_index`、`get_jiangqian12_start_index`。 ### 9. 推大限小限 [#9-推大限小限] ```python r = utils.get_decadals_and_ages(4, "wood3rd", "female", "gengHeavenly", "chenEarthly") print(r.decadals[0], r.ages[0]) ``` ```text Decadal(range=(43, 52), heavenly_stem='戊', heavenly_stem_key='wuHeavenly', earthly_branch='寅', earthly_branch_key='yinEarthly') [9, 21, 33, 45, 57, 69, 81, 93, 105, 117] ``` 这个函数直接收命宫索引与五行局,不必先凑出一份完整的出生数据, 能力是 iztro 对应函数的超集。见[从 iztro 迁移:API 对照](/zh/docs/guide/about/iztro-parity#大限小限)。 [Rust](/zh/docs/rust/util#get_decadals_and_ages) · [Python](/zh/docs/python/util#get_decadals_and_ages) · [Go](/zh/docs/go/util#getdecadalsandages) ## 哪些步骤会被配置改变 [#哪些步骤会被配置改变] | 配置 | 影响的步骤 | | ------------------ | ------------------------ | | `year_divide` | 1(年干支)→ 连带 6、7、8、9 | | `horoscope_divide` | 1(月柱与年系杂耀所用年支)→ 连带 7 | | `day_divide` | 1、2(晚子时归属)→ 连带 3、5 | | `age_divide` | 9(虚岁进位时点) | | `algorithm` | 4(中州派命主按年支)、7、8(部分星耀的取法) | | `astro_type` | 4 起(换宫起局)→ 连带 5、6、8、9 | | 自定义四化表 | 6(四化标记)与全部飞星判断 | | 自定义亮度表 | 6、7(星耀亮度) | 逐项说明见 [Config 详解](/zh/docs/guide/guides/config)。 # 给 AI 读的文档端点 (/zh/docs/guide/guides/llms-txt) 本站提供的 llms.txt、llms-full.txt、单页 Markdown 与 Accept 头协商。 *适合:开发者 · 运维* 这一页讲的是**怎么让 AI 读懂这份文档**——不是怎么用这个库。 想把 x-iztro 接进 AI 应用,看[让 AI 解读命盘](/zh/docs/guide/guides/llm)。 ## `/llms.txt` [#llmstxt] 站点结构索引,列出该语言所有页面的标题、描述与链接。适合让模型先定位再抓取。 ## `/llms-full.txt` [#llms-fulltxt] 该语言全站文档的 Markdown 全文,一次抓取即可作为完整上下文。 ## 两个端点都分语言 [#两个端点都分语言] 索引与全文各语言一份,互不混杂——全文本来就是整份塞进上下文用的, 掺入用不上的语言只会挤占窗口。 | 端点 | 内容 | | ----------------------------------------- | ------------------------------ | | `/zh/llms.txt` · `/en/llms.txt` | 该语言的结构索引,末尾列出其余语言与全文端点 | | `/zh/llms-full.txt` · `/en/llms-full.txt` | 该语言的全文 | | `/llms.txt` · `/llms-full.txt` | llms.txt 约定的根路径,内容同 `/zh/` 那两个 | ## 单页 Markdown [#单页-markdown] 任意文档页 URL 追加 `.md` 就得到该页的 Markdown 原文: `.md` 与 `.mdx` 两种后缀都可以,返回内容相同。 页面标题下方的「复制 Markdown」按钮取的就是这个端点,「打开」下拉里还能 直接送进 ChatGPT 或 Claude。 ## 不知道后缀约定也能拿到 [#不知道后缀约定也能拿到] AI 代理在请求任意文档页时,只要 `Accept` 头里表明更想要 Markdown, 就会拿到 Markdown 原文而不是整页 HTML: ```bash curl -H "Accept: text/markdown" <本站地址>/zh/docs/rust/palace ``` 同一个页面,HTML 约 400 KB,Markdown 约 14 KB。 这三种取法返回的都是纯文本,没有导航、样式与脚本, 比让模型抓 HTML 省 token 也更准确。 ## 建议的用法 [#建议的用法] 问 AI 关于 x-iztro 的问题时,把 `/zh/llms-full.txt` 作为上下文一起给它。 单一语言的全文约 500 KB,在常见模型的上下文窗口内绰绰有余。 如果只关心某个主题,抓对应的单页 `.md` 更省: | 主题 | 页面 | | --------- | ------------------------------------ | | 参数与安装 | `/zh/docs/guide/getting-started.md` | | 领域概念 | `/zh/docs/guide/concepts.md` 及其子页 | | 分界点与流派 | `/zh/docs/guide/guides/config.md` | | 字段字典 | `/zh/docs/guide/data-model.md` | | Prompt 格式 | `/zh/docs/guide/guides/ai-prompt.md` | # 数据结构字典 (/zh/docs/guide/data-model) Astrolabe、Palace、Star、Horoscope 每一个字段的类型与含义。 *适合:开发者* 这一页以序列化后的 JSON 字段名(camelCase)为准,它是三套绑定共用的契约。 各编程语言的命名换算: | 层 | 命名 | 示例 | | ----------- | ---------------- | ---------------- | | JSON / 绑定契约 | camelCase | `isBodyPalace` | | Python | snake\_case | `is_body_palace` | | Go | PascalCase | `IsBodyPalace` | | Rust | snake\_case,值为枚举 | `is_body_palace` | ## Astrolabe 星盘 [#astrolabe-星盘] 排盘入口的返回值。 | 字段 | 类型 | 含义 | | ------------------------------ | --------------------------- | --------------------- | | `gender` | string | 性别,翻译文本 | | `genderKey` | string | `"male"` / `"female"` | | `solarDate` | string | 阳历生日,与入参一致 | | `lunarDate` | string | 农历生日的文字表示 | | `chineseDate` | string | 四柱干支展示串 | | `rawDates` | [RawDates](#rawdates-结构化日期) | 结构化的农历生日与四柱 | | `time` | string | 时辰名,如「寅时」 | | `timeRange` | string | 时辰时间段,如「03:00\~05:00」 | | `sign` | string | 星座 | | `zodiac` | string | 生肖,按年支 | | `earthlyBranchOfSoulPalace` | string | 命宫地支 | | `earthlyBranchOfSoulPalaceKey` | string | 命宫地支 key | | `earthlyBranchOfBodyPalace` | string | 身宫地支 | | `earthlyBranchOfBodyPalaceKey` | string | 身宫地支 key | | `soul` | string | 命主星 | | `soulKey` | string | 命主星 key | | `body` | string | 身主星 | | `bodyKey` | string | 身主星 key | | `fiveElementsClass` | string | 五行局 | | `fiveElementsClassKey` | string | 五行局 key,如 `water2nd` | | `palaces` | [Palace](#palace-宫位)\[12] | 十二宫,索引 0 是寅宫 | | `timeIndex` | int | 出生时辰索引 0–12,保留原始传入值 | | `fixLeap` | bool | 是否修正闰月 | | `language` | string | 盘面语言 | | `config` | [Config](#config-配置) | 排盘配置 | ## Palace 宫位 [#palace-宫位] | 字段 | 类型 | 含义 | | ---------------------------------- | ---------------------- | ---------------------------------------------------------------------------- | | `index` | int | 宫位在盘上的位置,0–11,0 是寅宫 | | `name` | string | 宫名 | | `nameKey` | string | 宫名 key,如 `soulPalace` | | `isBodyPalace` | bool | 是否身宫 | | `isOriginalPalace` | bool | 是否来因宫 | | `heavenlyStem` | string | 宫干 | | `heavenlyStemKey` | string | 宫干 key | | `earthlyBranch` | string | 宫支 | | `earthlyBranchKey` | string | 宫支 key | | `majorStars` | [Star](#star-星耀)\[] | 主星 | | `minorStars` | [Star](#star-星耀)\[] | 辅星 | | `adjectiveStars` | [Star](#star-星耀)\[] | 杂耀 | | `changsheng12` / `changsheng12Key` | string | 长生十二神 | | `boshi12` / `boshi12Key` | string | 博士十二神 | | `jiangqian12` / `jiangqian12Key` | string | 将前十二神 | | `suiqian12` / `suiqian12Key` | string | 岁前十二神 | | `mutagenStarKeys` | string\[4] | 本宫宫干化出的四颗星标识,顺序为禄、权、科、忌;受[自定义四化表](/zh/docs/guide/guides/config#自定义四化表与亮度表)影响 | | `decadal` | [Decadal](#decadal-大限) | 该宫掌管的大限 | | `ages` | int\[] | 小限经过该宫的虚岁列表 | ## Star 星耀 [#star-星耀] | 字段 | 类型 | 含义 | | --------------- | ------- | ----------------------------------------------------------------------------------- | | `key` | string | 星耀 key,如 `ziweiMaj` | | `name` | string | 星名 | | `type` | string | `major` / `soft` / `tough` / `adjective` / `flower` / `helper` / `lucun` / `tianma` | | `scope` | string | `origin` / `decadal` / `yearly` / `monthly` / `daily` / `hourly` | | `brightness` | string | 亮度显示文本。**主星与辅星恒有这个键**,无亮度时为空串;杂耀与流耀整个键缺省 | | `brightnessKey` | string? | 亮度标识。无亮度时**缺省**(不是空串) | | `mutagen` | string | 四化显示文本。**十四主星与左辅右弼文昌文曲这 18 颗四化候选星恒有这个键**,无四化时为空串;其余星整个键缺省 | | `mutagenKey` | string? | 四化标识。无四化时**缺省** | `brightness` / `mutagen` 这两个**翻译字段**按星耀类别决定键在不在, 在的时候可能是空串;`brightnessKey` / `mutagenKey` 这两个**标识字段** 则是没值就整个键不出现。 所以「有没有亮度」要判断 `brightnessKey` 存在与否, 而不是判断 `brightness` 这个键在不在 —— 后者对所有主辅星都为真。 ## Decadal 大限 [#decadal-大限] | 字段 | 类型 | 含义 | | ------------------------------------ | ----------- | -------- | | `range` | \[int, int] | 起止虚岁,含两端 | | `heavenlyStem` / `heavenlyStemKey` | string | 大限天干 | | `earthlyBranch` / `earthlyBranchKey` | string | 大限地支 | ## RawDates 结构化日期 [#rawdates-结构化日期] | 字段 | 类型 | 含义 | | ------------------------- | ----------------- | ----------------------------------------- | | `lunarDate.lunarYear` | int | 农历年 | | `lunarDate.lunarMonth` | int | 农历月 1–12 | | `lunarDate.lunarDay` | int | 农历日 1–30 | | `lunarDate.isLeap` | bool | 是否闰月 | | `chineseDate.yearly` | \[string, string] | 年柱 \[天干, 地支] | | `chineseDate.monthly` | \[string, string] | 月柱 | | `chineseDate.daily` | \[string, string] | 日柱 | | `chineseDate.hourly` | \[string, string] | 时柱 | | `chineseDate.yearlyKeys` | \[string, string] | 年柱的[语言无关 key](/zh/docs/guide/guides/keys) | | `chineseDate.monthlyKeys` | \[string, string] | 月柱的语言无关 key | | `chineseDate.dailyKeys` | \[string, string] | 日柱的语言无关 key | | `chineseDate.hourlyKeys` | \[string, string] | 时柱的语言无关 key | 四柱里的干支是未本地化的原文(任何盘面语言下都是中文),做判断请用 `*Keys`。 把 `*Keys` 交给 `translate_chinese_date` 即可得到按语言翻译的展示串, 与顶层 `chineseDate` 字段逐字一致。 ## Config 配置 [#config-配置] | 字段 | 取值 | 默认 | | ----------------- | ---------------------------- | --------- | | `yearDivide` | `normal` / `exact` | `normal` | | `horoscopeDivide` | `normal` / `exact` | `normal` | | `ageDivide` | `normal` / `birthday` | `normal` | | `dayDivide` | `forward` / `current` | `forward` | | `algorithm` | `default` / `zhongzhou` | `default` | | `astroType` | `heaven` / `earth` / `human` | `heaven` | 另有两个**只进不出**的输入键,用于替换内置数据表: | 输入键 | 取值 | | ------------ | ---------------------------------------- | | `mutagens` | `{天干标识: [四个星耀标识]}`,顺序为禄权科忌,必须四项 | | `brightness` | `{星耀标识: [十二个亮度标识]}`,第一项是寅宫,必须十二项,无亮度处传空串 | 这两个键**不会回显在星盘的 `config` 里** —— 它们是排盘的输入配置, 不属于排盘结果,加进 DTO 会破坏与 JS iztro 的字段契约。 要留档就自己存那份配置。 含义见 [Config 详解](/zh/docs/guide/guides/config)。 ## Horoscope 运限 [#horoscope-运限] | 字段 | 类型 | 含义 | | ----------- | -------------------------------------- | -------------------- | | `solarDate` | string | 目标阳历日期 | | `lunarDate` | string | 目标农历日期 | | `decadal` | [HoroscopeScope](#horoscopescope-运限层级) | 大限,未起运时为童限 | | `age` | HoroscopeScope | 小限,带 `nominalAge` | | `yearly` | HoroscopeScope | 流年,带 `yearlyDecStar` | | `monthly` | HoroscopeScope | 流月 | | `daily` | HoroscopeScope | 流日 | | `hourly` | HoroscopeScope | 流时 | ### HoroscopeScope 运限层级 [#horoscopescope-运限层级] | 字段 | 类型 | 含义 | | ------------------------------------ | -------------------------------------- | ---------------------------------------- | | `index` | int | 该运限所在盘上位置 0–11 | | `name` | string | 层级名,翻译文本 | | `heavenlyStem` / `heavenlyStemKey` | string | 该运限天干 | | `earthlyBranch` / `earthlyBranchKey` | string | 该运限地支 | | `palaceNames` | string\[12] | 以该运限位置为命宫重排的宫名,按盘上位置排列 | | `palaceNameKeys` | string\[12] | 同上的 key 形式 | | `mutagen` | string\[4] | 四化星名,顺序为禄、权、科、忌 | | `mutagenKeys` | string\[4] | 同上的 key 形式 | | `stars` | [Star](#star-星耀)\[]\[12]? | 流耀在十二宫的分布(外层十二项对应宫位,内层是该宫的流耀列表),无流耀的层级缺省 | | `nominalAge` | int? | 虚岁,仅小限有 | | `yearlyDecStar` | [YearlyDecStar](#yearlydecstar-流年十二神)? | 仅流年有 | ### YearlyDecStar 流年十二神 [#yearlydecstar-流年十二神] | 字段 | 类型 | 含义 | | --------------------------------- | ----------- | ------------------- | | `suiqian12` / `suiqian12Keys` | string\[12] | 按流年支起的岁前十二神,索引即宫位索引 | | `jiangqian12` / `jiangqian12Keys` | string\[12] | 按流年支起的将前十二神 | Rust 里 `age` 与 `yearly` 的通用字段收在 `.base` 下(`AgeItem { base, nominal_age }`), 序列化时用 `#[serde(flatten)]` 展平,所以 JSON 与 Python / Go 侧看到的是平铺结构。 ## 完整 JSON 样例 [#完整-json-样例] `by_solar("2000-8-16", 2, female)` 的真实输出(顶层,省略 `palaces` 的十二项): ```json { "gender": "女", "genderKey": "female", "solarDate": "2000-8-16", "lunarDate": "二〇〇〇年七月十七", "chineseDate": "庚辰 甲申 丙午 庚寅", "rawDates": { "lunarDate": { "lunarYear": 2000, "lunarMonth": 7, "lunarDay": 17, "isLeap": false }, "chineseDate": { "yearly": ["庚", "辰"], "monthly": ["甲", "申"], "daily": ["丙", "午"], "hourly": ["庚", "寅"], "yearlyKeys": ["gengHeavenly", "chenEarthly"], "monthlyKeys": ["jiaHeavenly", "shenEarthly"], "dailyKeys": ["bingHeavenly", "wuEarthly"], "hourlyKeys": ["gengHeavenly", "yinEarthly"] } }, "time": "寅时", "timeRange": "03:00~05:00", "sign": "狮子座", "zodiac": "龙", "earthlyBranchOfSoulPalace": "午", "earthlyBranchOfSoulPalaceKey": "wuEarthly", "earthlyBranchOfBodyPalace": "戌", "earthlyBranchOfBodyPalaceKey": "xuEarthly", "soul": "破军", "soulKey": "pojunMaj", "body": "文昌", "bodyKey": "wenchangMin", "fiveElementsClass": "木三局", "fiveElementsClassKey": "wood3rd", "palaces": [ /* 12 项 */ ], "timeIndex": 2, "fixLeap": true, "language": "zh-CN", "config": { "yearDivide": "normal", "horoscopeDivide": "normal", "ageDivide": "normal", "dayDivide": "forward", "algorithm": "default", "astroType": "heaven" } } ``` ### 一个宫的样例 [#一个宫的样例] 同一张盘的命宫(`palaces` 里 `index` 为 4 的那一项): ```json { "index": 4, "name": "命宫", "nameKey": "soulPalace", "isBodyPalace": false, "isOriginalPalace": false, "heavenlyStem": "壬", "heavenlyStemKey": "renHeavenly", "earthlyBranch": "午", "earthlyBranchKey": "wuEarthly", "majorStars": [ { "key": "ziweiMaj", "name": "紫微", "type": "major", "scope": "origin", "brightness": "庙", "brightnessKey": "miao", "mutagen": "" } ], "minorStars": [ { "key": "wenquMin", "name": "文曲", "type": "soft", "scope": "origin", "brightness": "陷", "brightnessKey": "xian", "mutagen": "" } ], "adjectiveStars": [ { "key": "fengge", "name": "凤阁", "type": "adjective", "scope": "origin" }, { "key": "tianfu", "name": "天福", "type": "adjective", "scope": "origin" }, { "key": "jielu", "name": "截路", "type": "adjective", "scope": "origin" }, { "key": "feilian", "name": "蜚廉", "type": "adjective", "scope": "origin" }, { "key": "nianjie", "name": "年解", "type": "helper", "scope": "origin" } ], "changsheng12": "衰", "changsheng12Key": "shuai", "boshi12": "青龙", "boshi12Key": "qinglong", "jiangqian12": "灾煞", "jiangqian12Key": "zhaisha", "suiqian12": "丧门", "suiqian12Key": "sangmen", "mutagenStarKeys": ["tianliangMaj", "ziweiMaj", "zuofuMin", "wuquMaj"], "decadal": { "range": [3, 12], "heavenlyStem": "壬", "heavenlyStemKey": "renHeavenly", "earthlyBranch": "午", "earthlyBranchKey": "wuEarthly" }, "ages": [5, 17, 29, 41, 53, 65, 77, 89, 101, 113] } ``` 紫微与文曲都有 `mutagen: ""` —— 它们是四化候选星,这一盘上没被化到, 所以键在但值为空,`mutagenKey` 则整个缺省。五颗杂耀连 `brightness` 键都没有。 ## `*Key` 字段是什么 [#key-字段是什么] 每个会被翻译的字段旁边都有一个同名加 `Key` 后缀的伴生字段, 取值是 iztro 的 i18n 键名,与盘面语言无关: ```json { "name": "紫微", "key": "ziweiMaj", "brightness": "庙", "brightnessKey": "miao" } ``` 翻译字段给人看,标识字段给代码用。星耀的标识字段直接叫 `key`(没有 `nameKey`), 其余一律是「原字段名 + Key」;数组形式的用复数 `Keys` (`mutagenKeys`、`palaceNameKeys`、`yearlyKeys`)。 `*Key` / `key` 系列、`genderKey`、`timeIndex`、`fixLeap`、`language`、`config` 是 x-iztro 相对 JS iztro 的扩展;其余字段与 iztro 的 `JSON.stringify` 输出逐键逐值一致,由绑定契约测试守着。判断逻辑请用标识字段,见 [key 契约](/zh/docs/guide/guides/keys)。 ## 导出 JSON [#导出-json] Python 侧有现成的导出方法,输出即上面这份契约: ```python chart.to_dict() # dict chart.to_json(indent=2) # str ``` `Astrolabe`、`Palace`、`Star` 之间有回指引用(宫位持有所属星盘), `asdict()` 会递归进去直到 `RecursionError`。要 JSON 就用 `to_json()`。 # 概览 (/zh/docs/guide/about) 准确性保证、给 AI 读的文档端点,以及移植与架构说明。 *适合:所有人* ## 项目信息 [#项目信息] | 项目 | 值 | | --------- | ---------------------------------------------------------------- | | 对照的 iztro | v2.5.8(版本锁定) | | 许可 | MIT | | 仓库 | [github.com/x-haose/x-iztro](https://github.com/x-haose/x-iztro) | | crates.io | [x-iztro](https://crates.io/crates/x-iztro) | | PyPI | [x-iztro](https://pypi.org/project/x-iztro/) | 当前版本号以 crates.io 与 PyPI 上的发布为准。 # 准确性保证 (/zh/docs/guide/about/accuracy) 约 71 万例金标测试如何保证 x-iztro 与 JS iztro 零差异,以及这个「准」的边界在哪。 *适合:所有人。「哈希比对怎么做」一节给开发者* 排盘库最重要的属性是**结果正确**。而「正确」在紫微斗数里没有权威裁判 —— 不同实现之间的差异往往来自流派取舍,很难说谁对谁错。 x-iztro 因此把目标定得很具体:**与 JS [iztro](https://github.com/SylarLong/iztro) v2.5.8 逐字段一致**。 把它当作金标准,差异就从「见仁见智」变成了可以自动检测的 bug。 ## iztro 是什么,为什么拿它当金标准 [#iztro-是什么为什么拿它当金标准] iztro 是一个 TypeScript 写的开源紫微斗数排盘库, 是这个领域里最完整、维护时间最长的开源实现之一, 不少前端项目与小程序在用。 选它做基准的理由不是「它一定对」,而是三条工程上的性质: 1. **完整**:本命盘、六层运限、四组十二神、年系杂耀、中州派、六种盘面语言, 一个不缺 —— 有得可对,才对得下去。 2. **确定**:同样的输入永远给同样的输出,没有随机与外部依赖, 所以差异一定是逻辑差异,不是噪声。 3. **可锁版本**:把版本钉在 v2.5.8,基准就是稳定的; iztro 升级时重新生成基准数据,失败的用例清单就是版本间的行为差异清单。 ## 这个「准」指什么、不指什么 [#这个准指什么不指什么] x-iztro 保证的是:**在同一套流派取舍下,算得与一个成熟实现完全一样**。 它**不保证**这套流派取舍本身是「对的」。 庚干化科取太阴还是天府、年干支按正月初一还是立春换、晚子时归今天还是明天 —— 这些历来就有分歧,iztro 选了一套,x-iztro 原样跟随,并把有分歧的地方 做成[配置开关](/zh/docs/guide/guides/config)让你自己决定。 如果你的流派与默认不同,改配置或用自定义四化表,别期待默认输出符合你的师承。 基准数据由 JS 侧生成,用例集中在 JS 实现能稳定生成的年份区间内, 边界年代(1583–1983 与 2044–2100)另有按十年抽样的一层。 x-iztro 本身支持公历 1583–9999 年,区间之外的年份能排出盘, 但**没有金标数据逐例对照过** —— 用在极端年份上时请自行验证。 ## 覆盖矩阵 [#覆盖矩阵] 全部基准数据由锁定版本的 JS iztro 生成,共约 71 万例: | 层级 | 用例数 | 覆盖范围 | 数据格式 | | --------- | ----------- | ------------------------------------------------------------- | ----------- | | Tier 1 | 1,560 | 60 年 × 13 时辰 × 男女,**全字段逐一比对**(含展示字段、来因宫与结构化日期) | 完整 JSON | | Tier 2 | 37,440 | 60 年 × 每月 1/15 号 × 13 时辰 × 男女 | 压缩 JSON | | Tier 3 | 586,430 | 60 年**每一天** × 13 时辰 × 男女 × fix\_leap(闰月双份) | SHA-256 CSV | | 边界年代 | 46,228 | 1583–1983 与 2044–2100 每 10 年抽样,补 Tier 1/2/3 只覆盖 1984–2043 的盲区 | SHA-256 CSV | | Horoscope | 5,760 | 360 命盘 × 16 目标日期,六层级运限全字段 | 紧凑 JSON | | Variants | 14,268 | by\_lunar 闰月逐日、中州派、六种盘面语言 | CSV / JSON | | Config | 9,696 | 四个分界开关的非默认取值,含排盘层与运限层的组合 | CSV / JSON | | 中州派盘型 | 12,488 | 天盘 / 地盘 / 人盘 | SHA-256 CSV | | **合计** | **713,870** | | | 以下不计入上表: * **翻译反查 1,559 例**:逐条对照 iztro `kot` 的实际取值,守的是同形译名的消歧顺序 * **绑定契约 13 例**:把 DTO 与 iztro 的 `JSON.stringify` 输出逐键逐值对照 * **Python 端到端 172 例**(含自定义四化表与亮度表、全时辰覆盖) * **Go 端到端测试**:金标对照、星耀落宫、并发正确性、覆盖表、非法输入轰炸 * **C FFI 边界安全测试**:任何非法输入都必须返回错误 JSON 而非崩溃 * **Prompt 快照测试**:中英两种语言的本命与运限 prompt 逐字节比对 ## 每一层在防什么 [#每一层在防什么] **Tier 1** 抓字段级差异。它比对包括展示串、来因宫标记在内的每一个字段, 一旦某个字段的翻译或格式与 iztro 不同,立刻暴露。 **Tier 2 与 Tier 3** 抓边界日期。紫微斗数的错误常常只在特定日期出现 —— 闰月、月末、年初、节气交接。Tier 3 覆盖 60 年里的每一天,一天都不漏。 **边界年代**抓年份两端。Tier 1/2/3 集中在 1984–2043, 这一层按十年抽样把范围拉到 1583 与 2100, 防的是历法算法在远端年份上悄悄走样。 **Horoscope** 抓运限。16 个目标日期专门选在会出问题的位置: 12 个流年支各一、童限、高龄、闰月、晚子时。 **Variants** 抓流派与盘面语言。中州派与六种盘面语言各自完整比对, 确保切换算法派别或语言不引入偏差。 **Config** 与**中州派盘型**抓分界点与盘型。立春窗口逐日、晚子时、生日前后 —— 每个开关都在它会产生分歧的窗口里逐日验证。 ## 哈希比对怎么做的 [#哈希比对怎么做的] 给开发者 Tier 3 有 58 万例,存完整 JSON 会有几十 GB。所以这几层比对的是**规范化串的 SHA-256**: JS 侧的 `tests/golden/canonical.mjs` 与 Rust 侧的 `tests/common/mod.rs` 实现同一套序列化规则,**逐字节同构**。两边各自把排盘结果压成同一个规范化串, 比对哈希即可 —— 存的是 64 个十六进制字符,而不是几十 KB 的 JSON。 哈希不一致时,用生成器的 `--inspect` 系列参数重放该例的 JS 输出, 与 Rust 的规范化串做 diff,直接定位到出错字段。 ## 跑测试 [#跑测试] ```bash # 常规层:单元 + Tier 1/2 + 运限 + 变体 + 配置 + 契约,约 15 秒 cargo test # Tier 3 全量:586,430 例,约 20 秒 cargo test --release --test golden_tier3 -- --ignored # 绑定端到端 cd python && pytest tests/ # 需先 maturin develop cd go/iztro && go test ./... ``` ## 重新生成基准数据 [#重新生成基准数据] 需要 Node.js 环境: ```bash cd tests/golden npm install node generate_tier1.mjs # → tier1_data.json node generate_tier2.mjs # → tier2/year_*.json(60 个文件) node generate_tier3.mjs # → tier3/year_*.csv(60 个文件,约 30 分钟) node generate_horoscope.mjs # → horoscope_data.json node generate_variants.mjs node generate_config.mjs ``` ## 跟进 iztro 新版本 [#跟进-iztro-新版本] 流程是固定的: 1. 升级 `tests/golden/package.json` 里锁定的 iztro 版本 2. 重新生成全部基准数据 3. 跑 `cargo test` 失败的用例清单就是两个版本之间的行为差异清单 —— 不需要读 changelog, 测试直接告诉你哪些字段变了。 这条流程覆盖的是**数值**差异。iztro 新增或删除 API 时测试不会报, 那部分要按[从 iztro 迁移:API 对照](/zh/docs/guide/about/iztro-parity)一页逐条自查。 ## 零容忍的含义 [#零容忍的含义] 任何一例不一致都当作 bug 处理,不接受「差异很小」「这个字段不重要」这类理由。 凡是 iztro 有的功能与数据,x-iztro 必须给出相同结果; 在此之上再谈扩展功能(语言无关标识、Prompt 生成、Config 开关的语义化)。 # 架构 (/zh/docs/guide/about/architecture) 核心层与三套绑定的分层、各自的实现取舍,以及不 panic 的设计约束。 *适合:开发者* x-iztro 是一份 Rust 核心加三套绑定。算法只实现一次, Python、Go 与 C 调用方拿到的是同一份计算结果。 ## 分层 [#分层] ``` ┌──────────────────────────────┐ │ Rust 核心库 │ │ astro/ 排盘、运限、宫位推算 │ │ star/ 安星 │ │ data/ 枚举、常量、数据表 │ │ i18n/ 六语言词表与双向查找 │ └──────────────┬───────────────┘ │ bridge.rs(编组与分派) dto.rs(序列化契约) │ ┌────────────────────┼────────────────────┐ │ │ │ python.rs wasm.rs ffi.rs PyO3 扩展 wasm32-wasip1 C ABI │ │ │ Python 包 Go 包(wazero) C / C++ / 其他 ``` 两个共用层各司其职: | 层 | 职责 | | ----------- | --------------------------------------------------------------------- | | `bridge.rs` | 入参解析、按名分派、结果编组。Python 与 Go 走同一个函数,行为没有分叉的余地。它是 crate 内部模块,不在公开 API 面上 | | `dto.rs` | 序列化契约:camelCase 键、按盘面语言翻译的值,外加 `*Key` 标识与排盘上下文 | 绑定文件因此很薄——只剩语言特有的部分:wasm 的内存协定、PyO3 的异常类型。 ## 三套绑定的取舍 [#三套绑定的取舍] ### Python:PyO3 原生扩展 [#pythonpyo3-原生扩展] Rust 侧用 pythonize 在 Python 对象与 Rust 结构体之间直转, Python 侧用 dataclass 包装成类型化 API。 * 编译为 abi3 wheel(`abi3-py310`),一个 wheel 覆盖 Python 3.10 及以上 * 零运行期依赖,纯 stdlib(dataclasses + StrEnum) * 没有 JSON 序列化往返,开销最小 ### Go:内嵌 WebAssembly [#go内嵌-webassembly] 编译为 `wasm32-wasip1`,用纯 Go 的 wazero 运行时执行。 选它而不是 cgo 的理由是**保留 Go 的交叉编译能力**:cgo 会让 `GOOS`/`GOARCH` 交叉编译变得极其麻烦,还要求使用者本机有 C 工具链。wasm 方案下 `go get` 即用, 静态链接与容器构建都不受影响。 单个 wasm 实例不能并发使用,包内维护一个**实例池**(上限 `GOMAXPROCS`): 每个调用取一个空闲实例,用完归还,多 goroutine 之间不串行化。 wasm 模块只编译一次,编译产物落盘缓存在 `os.UserCacheDir()` 下, 所以只有机器上第一次是 \~200ms,之后每个进程的首次调用 \~30ms。 `iztro.Warmup(ctx)` 可以把这段冷启动提前到服务启动阶段, `iztro.Close(ctx)` 归还全部实例内存。 wazero 的编译器后端只支持 amd64 与 arm64,其余架构走解释器,速度慢但结果相同。 每次调用另有一次 JSON 编解码与 wasm 内存拷贝,热路径上单次排盘在 0.5ms 量级。 ### C FFI [#c-ffi] 标准 C ABI,收 C 字符串、返回 JSON 字符串。 错误以 `{"error":"..."}` 返回,由 serde 生成以保证转义完备。 外层有 `catch_unwind` 兜底。 ## 核心层不 panic [#核心层不-panic] 日期格式与存在性、公历年份范围、时辰索引在**核心层**校验,入口返回 `Result`; 性别、盘面语言、配置开关、标识这些以字符串传入的东西在绑定层解析时校验 (Rust 侧它们本来就是枚举)。两处都不 panic。 绑定层的 `catch_unwind` 只负责兜底库内部的缺陷,不承担参数校验职责。 wasm 上 panic 会变成 trap,而 `catch_unwind` 在 wasm 上无效——兜不住。 更糟的是每次 trap 都会永久损耗模块实例的栈空间,累积之后连合法调用都会失败。 校验因此必须在更靠内的一层,三种编程语言共用同一道防线。 各语言的错误类型见 [Rust](/zh/docs/rust/errors)、[Python](/zh/docs/python/errors)、[Go](/zh/docs/go/errors) 三页。 ## 一致性怎么保证 [#一致性怎么保证] 三种编程语言的行为一致不靠纪律,靠三层结构约束: **算法只有一份** —— 全部计算在 Rust 核心完成,绑定层不含任何斗数逻辑 **编组只有一份** —— Python 与 Go 调用同一个 `bridge::query` ,入参解析与结果形状不可能分叉 **断言成对** —— 每组对外能力在 Python 与 Go 两侧各有一组 parity 测试,断言同一张盘上的同一组取值 判断方法一律基于语言无关标识,因此同一条分析规则在三种编程语言上写出来、结果也相同。 标识约定见[语言无关标识](/zh/docs/guide/guides/keys)。 ## 版本 [#版本] | 项目 | 版本 | | ------------ | ------------ | | 对照的 iztro | v2.5.8(版本锁定) | | Rust edition | 2024 | | Python 要求 | 3.10 及以上 | | Go 要求 | 1.22 及以上 | 当前版本号见 [crates.io](https://crates.io/crates/x-iztro) 与 [PyPI](https://pypi.org/project/x-iztro/)。 ## 许可 [#许可] MIT。 # 从 iztro 迁移:API 对照 (/zh/docs/guide/about/iztro-parity) iztro 每个公开 API 在 x-iztro 三侧的落点,换了形状的几处及其原因,以及不提供的那些。 *适合:开发者,尤其是从 JS iztro 迁移过来的* x-iztro 是 [iztro](https://github.com/SylarLong/iztro) v2.5.8 的移植。 iztro 的每个公开 API 在 Rust、Python、Go 三侧都有等价物, 三侧能力完全一致,形式各随语言习惯。 这一页写给从 iztro 迁移过来的人:名字对不上时来这里查。 逐个 API 的用法见各语言的 API 参考。 ## 名字直接对得上的 [#名字直接对得上的] | iztro | Rust | Python | Go | | ------------------------- | ------------------------------ | -------------------------- | -------------------------- | | `astro.bySolar` | `by_solar` | `astro.by_solar` | `BySolar` | | `astro.byLunar` | `by_lunar` | `astro.by_lunar` | `ByLunar` | | `chart.horoscope` | `chart.horoscope` | `chart.horoscope` | `Horoscope` | | `chart.palace` | `chart.palace` | `chart.palace` | `Palace` / `PalaceByIndex` | | `chart.surroundedPalaces` | `chart.surrounded_palaces` | `chart.surrounded_palaces` | `SurroundedPalaces` | | `palace.fliesTo` | `flies_to` | `flies_to` | `FliesTo` | | `util.fixIndex` | `utils::fix_index` | `utils.fix_index` | `FixIndex` | | `star.getMajorStar` | `star::query::get_major_stars` | `star.get_major_star` | `GetMajorStar` | | `i18n.t` | `translate_key` | `i18n.translate` | `Translate` | | `i18n.kot` | `key_of` | `i18n.key_of` | `KeyOf` | 其余同理:JS 的 camelCase 在 Rust / Python 下是 snake\_case,在 Go 下是 PascalCase。 ## 换了形状的 [#换了形状的] 这几处不是照抄,因为照抄会把 JS 的限制一起搬过来。 ### 排盘视角(天盘 / 地盘 / 人盘) [#排盘视角天盘--地盘--人盘] iztro 把 `astroType` 放在 `astro.withOptions` 的选项对象上, 是因为它的 `astro.config()` 是全局单例、装不下按盘变化的值。 x-iztro 的配置本来就随每次排盘传入,因此 `astroType` 直接收进 `Config`, 两个排盘入口都能用,不必再记一个入口: ```python from x_iztro import Astro, ChartConfig chart = Astro().by_solar("2000-8-16", 2, "female", config=ChartConfig(astro_type="earth")) ``` 从任意干支起盘对应 `rearrangeAstrolable`,三侧都是星盘方法 `rearranged(干, 支)`。 ### 没有全局配置与全局语言 [#没有全局配置与全局语言] iztro 的 `astro.config()`、`i18n.setLanguage()` 改的是模块级单例, 因此还需要 `astro.getConfig()` 把值读回来。 x-iztro 没有全局状态:配置与语言都随每次调用传入,由调用方自己持有。 所以不提供 `getConfig` 与 `setLanguage` —— 想读回来,读你自己那份就是。 ### 大限小限 [#大限小限] `astro/palace` 的 `getHoroscope(param)` 收一份 `AstrolabeParam`。 x-iztro 的 `get_decadals_and_ages` 直接收命宫索引与五行局, 不必先凑出一份出生数据,能力是 iztro 那个的超集。 ### 农历入口的闰月参数 [#农历入口的闰月参数] `byLunar(lunarDateStr, timeIndex, gender, isLeapMonth?, fixLeap?, language?)` 用两个相邻的布尔 描述闰月:写反了不报错,盘会静默错一个月,而 `fixLeap` 又只在输入是闰月时才有意义。 x-iztro 把两者合成一个三态值:Rust `LeapMonth::{NotLeap, Leap, LeapFixed}`、 Go `NotLeapMonth / LeapMonthKeep / LeapMonthFixed`;Python 保留两个布尔但改为只能按关键字传入 (`is_leap_month=`、`fix_leap=`)。绑定层的 JSON 线协议仍是 `isLeapMonth`/`fixLeap` 两个键, 与 iztro 一致。阳历入口的 `fixLeap` 单独一个布尔,没有写反的余地,保持不变。 Go 侧同理把 `gender`、`language` 做成具名字符串类型 `Gender` / `Language` (`GenderFemale`、`LanguageZhCN`),字面量仍可直接传,但别的字符串变量传错位置会在编译期被挡下。 ### 插件 [#插件] iztro 的 `loadPlugin` / `use(plugin)` 是运行期往星盘对象上挂函数 —— 这是 JS 缺少其他扩展手段的产物。三侧各按语言给出的答案实现同一能力, 都是编译期或加载期完成,不牺牲类型检查: | | 做法 | | ------ | -------------------------------------------------------------------- | | Rust | 扩展 trait | | Python | `x_iztro.plugin` 的 `load_plugin` / `load_plugins`,往 `Astrolabe` 类挂方法 | | Go | 嵌入 `*Astrolabe`(Go 不允许给外部包的类型加方法,嵌入是语言给出的答案) | 写法见[扩展星盘](/zh/docs/guide/guides/plugins)。 ### 反查译名时的消歧 [#反查译名时的消歧] `kot(value, k)` 的第二个参数在三侧是独立入口: `key_of_in`(Rust)、`key_of(text, key_filter)`(Python)、`KeyOfIn`(Go)。 取值与 iztro 逐例一致,包括 `horse`、`dragon`、`유시` 这类同形译名落到哪个标识。 iztro 的 `kot` 查不到会把入参吐回来;x-iztro 返回 `None`(Rust / Python)或空串(Go)。 迁移时如果依赖过「查不到就当作原值继续用」这个行为,这里要改。 ## 不提供的 [#不提供的] | iztro | 为什么不做 | | -------------------------------------------------------------- | --------------------------------------------------------------- | | `astro.astrolabeBySolarDate` / `astrolabeByLunarDate` | iztro v2.0.5 起废弃的别名,与 `bySolar` / `byLunar` 同参同行为 | | `star.initStars` | JS 里是返回 12 个空数组的工厂;三侧的类型系统本身就给出定长 12 的数组 | | `util.fixEarthlyBranchIndex` | 与 `earthlyBranchIndexToPalaceIndex` 同义 | | `palace.setAstrolabe` / `star.setPalace` / `star.setAstrolabe` | 建立对象间引用是内部行为,三侧在解析后自动完成 | | `astro/analyzer` 模块 | 里面 11 个函数是宫位与三方四正方法的自由函数版(`hasStars` 即 `palace.has`),能力已由对象方法覆盖 | | `calendar` 模块 | 在 iztro v2.5.8 里已是死代码:活的代码路径全部改走 `lunar-lite` 依赖,该模块也不在包的根导出里 | | `i18n` 默认导出的 i18next 实例 | 不转手第三方库实例;翻译与反查由 `translate` / `key_of` 覆盖 | | `Astrolabe.copyright` | iztro 自身的版权声明字符串 | ## 行为上要注意的几处 [#行为上要注意的几处] ### 自定义四化表与亮度表只收标识 [#自定义四化表与亮度表只收标识] `Config` 的 `mutagens` / `brightness` 两张覆盖表**只接受语言无关标识**: `"ziweiMaj"` 可以,`"紫微"` 不行 —— 收译名会让配置绑死在某种盘面语言上。 ### 覆盖表长度严格校验 [#覆盖表长度严格校验] 四化表必须给满 4 项(禄权科忌),亮度表必须给满 12 项(第一项是寅宫), 多一项少一项都直接报 `invalid_argument`,不做补齐也不做截断。 见 [Config 详解](/zh/docs/guide/guides/config#自定义四化表与亮度表)。 ### 覆盖表不回显在输出的 config 里 [#覆盖表不回显在输出的-config-里] 星盘上回显的 `config` 只有六个开关。覆盖表是排盘的输入, 放进 DTO 会破坏与 iztro 的字段契约,所以读回来是空的 —— 要留档自己存。 ## 比 iztro 多的 [#比-iztro-多的] * **语言无关标识**:星盘每个字段在译名之外同时给出 `*key` / `*Key`, 取值是 iztro 的 i18n key。判断逻辑因此不受盘面语言影响, 不必再反查译名。详见[标识体系](/zh/docs/guide/guides/keys) * **Prompt 生成**:`astrolabe_to_prompt` / `horoscope_to_prompt` 把星盘或运限渲染成适合喂给大模型的纯文本,见 [AI Prompt 生成](/zh/docs/guide/guides/ai-prompt) * **入口前置校验**:非法日期、越界时辰等在入口返回错误而非 panic, 且带机器可读的分类码,见[错误处理](/zh/docs/guide/guides/errors) * **自定义四化表与亮度表**:按标识整表替换内置数据, 见 [Config 详解](/zh/docs/guide/guides/config#自定义四化表与亮度表) * **`all_keys`**:一次取全部 260 个可翻译标识 ## 数值一致性 [#数值一致性] 功能对齐之外,排盘结果与 iztro **逐字段零差异**,由约 71 万例金标数据守着。 覆盖矩阵与验证方式见[准确性保证](/zh/docs/guide/about/accuracy)。 # 概览 (/zh/docs/rust) crate 结构、类型体系与阅读本参考的方式。 x-iztro 的 Rust crate 是整个项目的核心,Python 与 Go 绑定都调用它。 这一栏是 Rust 侧的完整 API 参考——每个公开函数、类型与方法都有独立条目。 ## 安装 [#安装] ```toml title="Cargo.toml" [dependencies] x-iztro = "0.2" ``` crate 无默认 feature,直接可用。`python` feature 仅供构建 PyO3 扩展时启用,普通依赖方不需要。 ## 第一张盘 [#第一张盘] ```rust use x_iztro::*; fn main() -> Result<(), IztroError> { let chart = by_solar("2000-8-16", 2, Gender::Female, true, Language::ZhCN, Config::default())?; println!("{} {}", chart.solar_date, chart.lunar_date); // 2000-8-16 二〇〇〇年七月十七 let soul = chart.palace(Palace::Soul).unwrap(); println!("{}", soul.data().major_stars.iter().map(|s| s.name.as_str()).collect::>().join(" ")); // 紫微 Ok(()) } ``` ## crate 结构 [#crate-结构] | 模块 | 内容 | 本参考对应页 | | ----------------- | ------------------------------------------------------- | --------------------------------------------------------------------------------------- | | `x_iztro::astro` | 排盘、运限、宫位推算、轻量查询 | [排盘入口](/zh/docs/rust/astro)、[运限对象](/zh/docs/rust/horoscope)、[轻量查询](/zh/docs/rust/query) | | `x_iztro::models` | `Astrolabe`、`PalaceData`、`Star`、`HoroscopeData` 与三个视图类型 | [星盘对象](/zh/docs/rust/astrolabe) 起的四页 | | `x_iztro::star` | 安星:低层构件与按出生数据的入口 | [安星模块](/zh/docs/rust/star) | | `x_iztro::data` | 枚举、常量、星耀与干支数据表 | [数据表](/zh/docs/rust/data) | | `x_iztro::utils` | 索引换算、亮度与四化查表等工具函数 | [工具函数](/zh/docs/rust/util) | | `x_iztro::i18n` | 六语言词表、`translate_*` 与双向查找 | [翻译](/zh/docs/rust/i18n) | | `x_iztro::error` | `IztroError`、`BridgeError` | [错误处理](/zh/docs/rust/errors) | | `x_iztro::prompt` | `astrolabe_to_prompt`、`horoscope_to_prompt` | [排盘入口](/zh/docs/rust/astro#astrolabe_to_prompt--horoscope_to_prompt) | | `x_iztro::dto` | 跨语言绑定共用的序列化 DTO | [数据结构](/zh/docs/guide/data-model) | | `x_iztro::ffi` | C ABI 导出,供 Go/C 调用 | Rust 调用方不需要 | 绑定层共用的编组与分派(原 `x_iztro::bridge`)已收为 crate 内部,不在公开 API 面上。 `use x_iztro::*;` 拿到全部入口函数、数据结构、枚举与十二个 `translate_*`—— 本参考里所有不带模块前缀的名字都在其中。带模块前缀的(`utils::fix_index`、 `star::query::get_major_stars`、`data::stars::get_star_info`、 `astro::palace::get_decadals_and_ages`)是低层构件,按路径调用。 ## 两层 API [#两层-api] 同一件事在 crate 里往往有两个层次,选哪个取决于你手上有什么。 `by_solar` · `star::query::*` · `astro::query::*` `star::location::*` · `star::decorative::*` · `astro::palace::*` 从出生数据到安星中间量(生效时辰、农历年月日、两套年干支、月索引、命身宫、五行局) 的推算收在 `astro::context`,收出生数据的那层调它一次,把结果喂给低层构件。 因此两层的结果永远一致,自己拼安星流程时也不必从日期重推一遍。 ## 视图类型 [#视图类型] Rust 的数据结构本身不持有星盘,因此 `PalaceData` 无法直接回答「我的对宫是谁」。 crate 用三个视图类型在查询入口处把数据与星盘绑在一起: | 视图 | 由谁返回 | 解引用得到 | 额外能力 | | ------------------ | ---------------------- | ---------------- | --------------- | | `PalaceRef<'a>` | `chart.palace(...)` | `&PalaceData` | 对宫、三方四正、飞星、四化宫位 | | `StarRef<'a>` | `chart.star(...)` | `&Star` | 所在宫、对宫、三方四正 | | `HoroscopeRef<'a>` | `chart.horoscope(...)` | `&HoroscopeData` | 运限宫位查询不必再传星盘 | ```rust let soul = chart.palace(Palace::Soul).unwrap(); soul.data().name; // 通过 data() 取底层字段 soul.opposite_palace(); // 视图独有:对宫 soul.flies_to(Palace::Wealth, &[Mutagen::Lu]); ``` 三个视图都实现了 `Deref`,`soul.name` 与 `soul.data().name` 等价。 ## 条目怎么读 [#条目怎么读] 每个 API 条目按固定八段组织: **用途** —— 一句话说清它做什么 **斗数含义** —— 它在紫微斗数里对应什么概念(纯工程性的函数省略此段) **签名** —— 从源码原样摘出 **参数** —— 名、类型、是否必填、默认值、说明 **返回值** —— 类型与结构 **示例** —— 可直接运行的片段 **输出** —— 该示例的真实运行结果 **边界与陷阱** —— 空值、越界、配置影响、与其他 API 的相互作用 示例统一用同一张盘:**2000 年 8 月 16 日寅时女命**(`("2000-8-16", 2, Gender::Female)`), 方便跨页对照。这张盘的完整数据见[数据结构](/zh/docs/guide/data-model)。 # 排盘入口 (/zh/docs/rust/astro) by_solar、by_lunar、rearranged 与 JSON 便捷版本。 排盘是一切的起点:给出生日期、时辰、性别,得到一张 `Astrolabe`。 本页是四个排盘入口的完整参考。 收外部输入的入口(`by_solar`、`by_lunar`、两个 JSON 版本、`get_horoscope`)都返回 `Result`:日期格式与存在性、公历年份范围、时辰索引在核心层前置校验,非法输入返回 `IztroError` 而不是 panic。入参全是枚举、无非法值的函数(`rearranged`、 `astrolabe_to_prompt`)直接返回结果。错误类型见[错误处理](/zh/docs/rust/errors)。 *** ## by\_solar [#by_solar] **用途** 由公历日期排出本命盘。 **斗数含义** 紫微斗数以农历为算法基础,但绝大多数人只记得公历生日。 本函数先把公历转农历(含年干支、月干支、日干支、时干支四柱),再据此安星。 换年的时点受 `year_divide` 影响——正月初一与立春之间出生的人,两种配置会得到不同的年干支, 进而影响四化、命主身主与全部年系星。 **签名** ```rust pub fn by_solar( solar_date: &str, time_index: u8, gender: Gender, fix_leap: bool, language: Language, config: Config, ) -> Result ``` **参数** | 参数 | 类型 | 必填 | 默认 | 说明 | | ------------ | ---------- | -- | -- | ------------------------------------------------------ | | `solar_date` | `&str` | 是 | — | 公历日期,格式 `YYYY-M-D`,月日不必补零。支持 1583–9999 年 | | `time_index` | `u8` | 是 | — | 时辰索引 0–12。0 为早子时(00:00–01:00),12 为晚子时(23:00–24:00) | | `gender` | `Gender` | 是 | — | `Gender::Male` 或 `Gender::Female`。决定大限顺逆与长生、博士十二神的排列方向 | | `fix_leap` | `bool` | 是 | — | 是否调整农历闰月。为 `true` 时闰月十六日起按下月算(晚子时除外,见下) | | `language` | `Language` | 是 | — | 输出语言,影响 DTO 中所有译名字段;`*_key` 标识字段不受影响 | | `config` | `Config` | 是 | — | 排盘配置,六个开关加自定义表。取默认值用 `Config::default()` | **返回值** `Astrolabe`——十二宫、四柱、命主身主、五行局俱全的完整星盘。字段清单见[数据结构](/zh/docs/guide/data-model)。 **示例** ```rust use x_iztro::*; let chart = by_solar("2000-8-16", 2, Gender::Female, true, Language::ZhCN, Config::default())?; println!("{} | {} | {}", chart.solar_date, chart.lunar_date, chart.chinese_date); println!("{} {} {}", chart.sign, chart.zodiac, translate_five_elements_class(chart.five_elements_class, Language::ZhCN)); println!("命主 {} 身主 {}", translate_star(chart.soul, Language::ZhCN), translate_star(chart.body, Language::ZhCN)); ``` `five_elements_class`、`soul`、`body` 是强类型枚举而非字符串—— 判断时直接比较,要展示则经 `i18n::translate_*` 转成当前语言的文本。 **输出** ```text 2000-8-16 | 二〇〇〇年七月十七 | 庚辰 甲申 丙午 庚寅 狮子座 龙 木三局 命主 破军 身主 文昌 ``` **边界与陷阱** 子时横跨午夜,分早子时(00:00–01:00,属当日)与晚子时(23:00–24:00,属次日)。 两者的日柱不同,紫微起宫也可能差一天,因此必须区分,索引才有 13 个。 `day_divide` 配置可以把晚子时改判为当日,见 [Config 详解](/zh/docs/guide/guides/config)。 进位要同时满足四个条件:该农历月确实是闰月、`fix_leap` 为 `true`、 农历日大于 15、且时辰索引不是 12(晚子时)。四者缺一,月索引就按本月算。 因此只有农历闰月下半月出生的人,`true` 与 `false` 会得到不同的月索引, 进而影响左辅右弼与全部月系星。 1582 年格里历改革留下了不存在的日期空洞,底层历法库在这些日期上会 panic。 crate 因此把公历支持范围收在 1583–9999,超出范围返回 `IztroError::InvalidDate`。 星盘上所有判断方法(`has`、`flies_to`、`with_mutagen` 等)都基于语言无关标识, 换语言排盘不会改变任何判断结果,只改变 `name` 一类展示字段。 *** ## by\_lunar [#by_lunar] **用途** 由农历日期排出本命盘。 **斗数含义** 农历日期是斗数的原生输入,跳过公历转换这一步。 知道自己农历生日的人直接用它,结果与用对应公历日期调 `by_solar` 完全一致。 **签名** ```rust pub fn by_lunar( lunar_date: &str, time_index: u8, gender: Gender, leap: LeapMonth, language: Language, config: Config, ) -> Result ``` **参数** 除以下两项外,其余与 `by_solar` 相同;`by_solar` 的 `fix_leap` 在这里并入 `leap`。 | 参数 | 类型 | 必填 | 默认 | 说明 | | ------------ | ----------- | -- | -- | -------------------------------------------------------------------------------------------- | | `lunar_date` | `&str` | 是 | — | 农历日期,格式 `YYYY-M-D`,月份写正数(闰月由下一参数标记) | | `leap` | `LeapMonth` | 是 | — | `NotLeap` 非闰月;`Leap` 闰月、按闰月本身排;`LeapFixed` 闰月且十五之后视作次月(iztro `fixLeap`)。标为闰月但那年那月没有闰月时按普通月处理 | **返回值** 同 `by_solar`。 **示例** ```rust use x_iztro::*; let a = by_lunar("2000-7-17", 2, Gender::Female, LeapMonth::NotLeap, Language::ZhCN, Config::default())?; let b = by_solar("2000-8-16", 2, Gender::Female, true, Language::ZhCN, Config::default())?; assert_eq!(a.solar_date, b.solar_date); println!("{}", a.solar_date); ``` **输出** ```text 2000-8-16 ``` **边界与陷阱** `leap` 标为闰月但那个月并非闰月时,按普通月排盘,不报错(与 iztro 一致)。 如果需要严格校验,调用前先自行确认该年该月确实有闰月。 `LeapMonth::from_flags(is_leap_month, fix_leap)` 可从 iztro 风格的两个布尔换算。 *** ## rearranged [#rearranged] **用途** 以指定干支为命宫重排本盘,返回新盘;原盘不变。 **斗数含义** 中州派把同一组出生数据看作三张盘:天盘以命宫干支起五行局, 地盘以身宫干支起,人盘以福德宫干支起。起局的干支一变,五行局就变, 紫微天府落点、十二宫名、长生十二神、大限小限随之全部重算。 本方法把这个能力放开到**任意干支**,不限于那三种。 **签名** ```rust pub fn rearranged(&self, from_stem: HeavenlyStem, from_branch: EarthlyBranch) -> Astrolabe ``` **参数** | 参数 | 类型 | 必填 | 默认 | 说明 | | ------------- | --------------- | -- | -- | ------ | | `from_stem` | `HeavenlyStem` | 是 | — | 新命宫的天干 | | `from_branch` | `EarthlyBranch` | 是 | — | 新命宫的地支 | **返回值** 新的 `Astrolabe`。重算:命宫身宫、五行局、十四主星、十二宫名、长生十二神、大限小限, 以及随命宫挪位的天伤、天使、天才。沿用原盘:辅星、其余杂耀、博士十二神、岁前与将前十二神。 **示例** ```rust use x_iztro::*; let chart = by_solar("2000-8-16", 2, Gender::Female, true, Language::ZhCN, Config::default())?; // 从原盘身宫的干支起盘,等价于地盘 let body = chart.palaces.iter().find(|p| p.is_body_palace).unwrap(); let earth = chart.rearranged(body.heavenly_stem, body.earthly_branch); println!("天盘 {} → 地盘 {}", translate_five_elements_class(chart.five_elements_class, Language::ZhCN), translate_five_elements_class(earth.five_elements_class, Language::ZhCN)); ``` **输出** ```text 天盘 木三局 → 地盘 土五局 ``` **边界与陷阱** 天盘、地盘、人盘用 `Config::default().with_astro_type(AstroType::Earth)` 直接排即可, 两个排盘入口都支持。`rearranged` 是为「从任意干支起盘」准备的。 跟着走:命宫地支、身宫地支、五行局、命主星。命主星按命宫地支查表, 命宫既已挪位,取值随之更新。 不动:身主星。它按**出生年支**查表,与命宫位置无关,重排不改变出生年。 `algorithm` 设为中州派时命主星也改按年支取,此时它同样不随重排变化。 `rearranged` 返回新盘,`&self` 只读。同一张原盘可以连续重排出多个视角, 互不干扰。 *** ## by\_solar\_json / by\_lunar\_json [#by_solar_json--by_lunar_json] **用途** 排盘并直接返回 DTO 的 JSON 字符串,省掉调用方自己序列化。 **签名** ```rust pub fn by_solar_json( solar_date: &str, time_index: u8, gender: Gender, fix_leap: bool, language: Language, config: Config, ) -> Result pub fn by_lunar_json( lunar_date: &str, time_index: u8, gender: Gender, leap: LeapMonth, language: Language, config: Config, ) -> Result ``` **参数** 与对应的排盘函数完全相同。 **返回值** `String`——[DTO](/zh/docs/guide/data-model) 的 JSON 序列化结果, camelCase 键、值按 `language` 翻译,另带 `*Key` 语言无关标识。 **示例** ```rust use x_iztro::*; let json = by_solar_json("2000-8-16", 2, Gender::Female, true, Language::ZhCN, Config::default())?; let v: serde_json::Value = serde_json::from_str(&json)?; println!("{} {}", v["solarDate"], v["palaces"][0]["nameKey"]); ``` **输出** ```text "2000-8-16" "wealthPalace" ``` **边界与陷阱** 这两个函数只是 `by_solar(...)?.to_dto()` 加序列化的快捷方式。 Rust 侧要做进一步分析时用 `by_solar` 拿 `Astrolabe`,能用上全部查询方法; 只是要把结果丢给别的进程或前端时才用 JSON 版本。 *** ## get\_horoscope [#get_horoscope] **用途** 以某张本命盘为起点计算目标日期的运限。 **斗数含义** 运限是把大限、小限、流年、流月、流日、流时六个层级叠在本命盘上, 每一层各有自己的宫位起点、干支与流耀。 **签名** ```rust pub fn get_horoscope( astrolabe: &Astrolabe, solar_date: &str, time_index: u8, language: Language, ) -> Result ``` **参数** | 参数 | 类型 | 必填 | 默认 | 说明 | | ------------ | ------------ | -- | -- | ----------------------------------- | | `astrolabe` | `&Astrolabe` | 是 | — | 本命盘 | | `solar_date` | `&str` | 是 | — | 目标公历日期,格式 `YYYY-M-D`,支持 1583–9999 年 | | `time_index` | `u8` | 是 | — | 目标时辰索引 0–12 | | `language` | `Language` | 是 | — | 输出语言 | **返回值** `Result`。详见[运限对象](/zh/docs/rust/horoscope)。 **示例** ```rust use x_iztro::*; let chart = by_solar("2000-8-16", 2, Gender::Female, true, Language::ZhCN, Config::default())?; let h = get_horoscope(&chart, "2025-1-1", 0, Language::ZhCN)?; println!("大限宫位索引 {},流年干支 {:?}{:?}", h.decadal.index, h.yearly.heavenly_stem, h.yearly.earthly_branch); ``` **输出** ```text 大限宫位索引 2,流年干支 JiaChen ``` **边界与陷阱** 要连着做运限查询(取某层级的宫位、判断流耀)时,用星盘方法 `chart.horoscope(...)` 拿 `HoroscopeRef`——它同时持有本命盘, 查询不必再把星盘传进去。这里的自由函数只返回数据本身。 *** ## astrolabe\_to\_prompt / horoscope\_to\_prompt [#astrolabe_to_prompt--horoscope_to_prompt] **用途** 把星盘或运限渲染成适合喂给大模型的纯文本。 **签名** ```rust pub fn astrolabe_to_prompt(astrolabe: &Astrolabe, lang: Language) -> String pub fn horoscope_to_prompt( astrolabe: &Astrolabe, horoscope: &HoroscopeData, lang: Language, ) -> String ``` **参数** | 参数 | 类型 | 必填 | 默认 | 说明 | | ----------- | ---------------- | -- | -- | ------------------- | | `astrolabe` | `&Astrolabe` | 是 | — | 本命盘 | | `horoscope` | `&HoroscopeData` | 是 | — | `get_horoscope` 的结果 | | `lang` | `Language` | 是 | — | 输出语言,随之切换段落标题与星耀译名 | **返回值** `String`,分节的纯文本。 **示例** ```rust use x_iztro::*; let chart = by_solar("2000-8-16", 2, Gender::Female, true, Language::ZhCN, Config::default())?; print!("{}", astrolabe_to_prompt(&chart, Language::ZhCN)); ``` **输出** ```text === 基本信息 === 性别: 女 阳历: 2000-8-16 农历: 二〇〇〇年七月十七 干支: 庚辰 甲申 丙午 庚寅 时辰: 寅时 (03:00~05:00) 星座: 狮子座 生肖: 龙 命宫地支: 午 身宫地支: 戌 命主: 破军 身主: 文昌 五行局: 木三局 生年四化: 太阳禄, 武曲权, 太阴科, 天同忌 === 十二宫 === --- 财帛 --- 天干地支: 戊寅 大限: 43-52 小限虚岁: 9, 21, 33, 45, 57, 69, 81, 93, 105, 117 十二神: 绝, 飞廉, 吊客, 岁驿 主星: 武曲(得)[权], 天相(庙) 辅星: 天马 杂耀: 解神, 三台, 天寿, 天巫, 天厨, 阴煞, 天哭 (以下十一宫格式相同,此处从略) ``` 完整输出与逐字段说明见[生成 AI 提示词](/zh/docs/guide/guides/ai-prompt)。 这是 x-iztro 在 iztro 之外自加的功能,三语言均可用。 用法与提示词写法见[让 AI 解读命盘](/zh/docs/guide/guides/llm)。 # 星盘对象 (/zh/docs/rust/astrolabe) Astrolabe 的字段、定位方法与三方四正判断。 `Astrolabe` 是排盘的产物,也是一切查询的入口。它持有十二宫的全部数据, 以及四柱、命主身主、五行局这些盘级信息。 ```rust let chart = by_solar("2000-8-16", 2, Gender::Female, true, Language::ZhCN, Config::default())?; ``` 本页示例统一用 `Language::ZhCN` 排盘,因此输出里的展示值都是中文。 换成别的语言只改这些展示串,`*_key` 标识与所有判断方法的结果不变。 ## 字段 [#字段] | 字段 | 类型 | 说明 | | -------------- | -------- | ------------------------- | | `gender` | `Gender` | 性别 | | `solar_date` | `String` | 公历日期,与入参一致 | | `lunar_date` | `String` | 农历日期的中文写法,如「二〇〇〇年七月十七」 | | `chinese_date` | `String` | 四柱展示串,如「庚辰 甲申 丙午 庚寅」 | | `time` | `String` | 时辰名,如「寅时」 | | `time_range` | `String` | 时辰对应的钟点区间,如「03:00\~05:00」 | | `sign` | `String` | 星座,按公历日期 | | `zodiac` | `String` | 生肖,按年支 | 展示字段随 `language` 翻译。要做判断请用下一组的标识字段。 | 字段 | 类型 | 说明 | | ------------------------------- | ------------------- | ----------------- | | `earthly_branch_of_soul_palace` | `EarthlyBranch` | 命宫地支 | | `earthly_branch_of_body_palace` | `EarthlyBranch` | 身宫地支 | | `soul` | `StarKey` | 命主星 | | `body` | `StarKey` | 身主星 | | `five_elements_class` | `FiveElementsClass` | 五行局,决定大限起运岁数与紫微起宫 | 这些是强类型枚举,与语言无关,可直接比较。 | 字段 | 类型 | 说明 | | ----------- | ------------------ | ------------------------ | | `palaces` | `[PalaceData; 12]` | 十二宫,定长数组,索引 0 为寅宫、11 为丑宫 | | `raw_dates` | `RawDates` | 结构化的农历生日与四柱干支枚举 | `palaces` 的索引是**宫位索引**而非宫名顺序:`palaces[0]` 永远是寅宫, 命宫可能落在其中任何一格。取命宫用 `chart.palace(Palace::Soul)`。 `raw_dates` 是 `lunar_date` / `chinese_date` 两个展示串的数据形式, 要做日期运算或按干支查表时用它,不必解析中文串: ```rust pub struct RawDates { pub lunar_date: RawLunarDate, pub chinese_date: RawChineseDate, } pub struct RawLunarDate { pub lunar_year: i64, // 农历年 pub lunar_month: u32, // 农历月 1–12,是否闰月看 is_leap pub lunar_day: u32, // 农历日 1–30 pub is_leap: bool, // 是否闰月 } pub struct RawChineseDate { pub yearly: (HeavenlyStem, EarthlyBranch), // 年柱 pub monthly: (HeavenlyStem, EarthlyBranch), // 月柱 pub daily: (HeavenlyStem, EarthlyBranch), // 日柱 pub hourly: (HeavenlyStem, EarthlyBranch), // 时柱 } ``` 三个类型都在 crate 根重导出,`use x_iztro::*;` 即可用。 | 字段 | 类型 | 说明 | | ------------ | ---------- | ------------------------------------ | | `time_index` | `u8` | 出生时辰索引,即使 `day_divide` 把晚子时改判当日也保留原值 | | `fix_leap` | `bool` | 排盘时是否修正闰月 | | `language` | `Language` | 输出语言 | | `config` | `Config` | 排盘配置 | 运限与 Prompt 从这四项重新发起计算,因此不必再传一遍排盘参数。 *** ## palace [#palace] **用途** 按索引、宫名、身宫或来因宫取一宫。 **斗数含义** 十二宫是斗数的骨架。命宫定下后,其余十一宫按固定顺序逆时针排开。 「身宫」是十二宫之一同时被标记的那一宫,代表后天着力处; 「来因宫」是宫干与生年干相同的那一宫,代表事情的起因。 **签名** ```rust pub fn palace(&self, target: impl Into) -> Option> ``` **参数** | 参数 | 类型 | 必填 | 默认 | 说明 | | -------- | ------------------------- | -- | -- | ------- | | `target` | `impl Into` | 是 | — | 四种写法见下表 | `PalaceTarget` 的四个变体都有 `From` 实现,调用时直接写值即可: | 写法 | 例子 | 含义 | | --- | -------------------------------------- | --------------- | | 索引 | `chart.palace(0)` | 宫位索引 0–11,0 为寅宫 | | 宫名 | `chart.palace(Palace::Soul)` | 十二宫名之一 | | 身宫 | `chart.palace(PalaceTarget::Body)` | 带身宫标记的那一宫 | | 来因宫 | `chart.palace(PalaceTarget::Original)` | 宫干与生年干相同的那一宫 | **返回值** `Option>`。索引越界返回 `None`;宫名、身宫、来因宫三种写法在任何一张盘上都能定位到,不会是 `None`。 **示例** ```rust let zh = Language::ZhCN; let soul = chart.palace(Palace::Soul).unwrap(); println!("{} {}{}", translate_palace(soul.name, zh), translate_heavenly_stem(soul.heavenly_stem, zh), translate_earthly_branch(soul.earthly_branch, zh)); let body = chart.palace(PalaceTarget::Body).unwrap(); println!("身宫落在 {}", translate_palace(body.name, zh)); let original = chart.palace(PalaceTarget::Original).unwrap(); println!("来因宫是 {}", translate_palace(original.name, zh)); println!("寅宫是 {}", translate_palace(chart.palace(0).unwrap().name, zh)); ``` `PalaceData::name` 的类型是 `Palace` 枚举而非字符串,不能直接用 `{}` 打印—— 枚举是语言无关标识,展示时经 `translate_palace` 转成当前语言的文本。 `heavenly_stem`、`earthly_branch`、`five_elements_class` 等字段同理。 **输出** ```text 命宫 壬午 身宫落在 官禄 来因宫是 夫妻 寅宫是 财帛 ``` **边界与陷阱** 来因宫要求宫干与生年干相同,且该宫不在子、丑二宫。 十二宫的天干由五虎遁从寅宫起排,寅到酉这十宫刚好把十天干各走一遍, 子、丑两宫是第十一、十二格,重复了寅、卯的天干——正因为重复才被排除在外。 于是生年干在寅到酉之间必然命中且只命中一次:任何一张盘上来因宫都存在,且唯一。 十二宫名在一张盘上各出现一次,因此按宫名查找必然唯一。 身宫是**标记**不是宫名——身宫同时也是十二宫中的某一宫(例中的官禄宫)。 来因宫同理,例中落在夫妻宫。 *** ## star [#star] **用途** 按标识找到一颗星,得到能回溯所在宫的视图。 **签名** ```rust pub fn star(&self, key: StarKey) -> Option> ``` **参数** | 参数 | 类型 | 必填 | 默认 | 说明 | | ----- | --------- | -- | -- | -------------------------- | | `key` | `StarKey` | 是 | — | 星耀标识,如 `StarKey::ZiweiMaj` | **返回值** `Option>`。该星不在这张盘上时返回 `None`。 **示例** ```rust let zh = Language::ZhCN; let ziwei = chart.star(StarKey::ZiweiMaj).unwrap(); println!("{} 在 {}", ziwei.name, translate_palace(ziwei.palace().name, zh)); println!("对宫是 {}", translate_palace(ziwei.opposite_palace().name, zh)); println!("亮度 {:?} 四化 {:?}", ziwei.brightness, ziwei.mutagen); ``` `Star::name` 是 `String`(排盘时已按语言翻译好),可以直接打印; 宫名 `PalaceData::name` 是枚举,要经 `translate_palace`。 **输出** ```text 紫微 在 命宫 对宫是 迁移 亮度 Some(Miao) 四化 None ``` **边界与陷阱** 只在主星、辅星、杂耀三组里查找。长生十二神、博士十二神、岁前与将前十二神 是每宫一个的标记而非星耀列表,用 `palace.changsheng12` 一类字段直接取。 *** ## surrounded\_palaces [#surrounded_palaces] **用途** 取目标宫的三方四正。 **斗数含义** 三方四正是斗数最常用的取象范围:本宫、对宫(本宫 +6)、 官禄位(本宫 +4)、财帛位(本宫 +8)。四个宫合起来看,而不只看本宫, 是因为对宫与三合宫的星耀同样作用于本宫的事。 **签名** ```rust pub fn surrounded_palaces(&self, target: impl Into) -> Option> ``` **参数** 同 `palace`,四种定位写法都支持。 **返回值** `Option>`,含 `target` / `opposite` / `wealth` / `career` 四个 `&PalaceData`(不是 `PalaceRef`,字段可直接读,但没有对宫、飞星那些需要星盘上下文的方法)。 判断方法见[三方四正](/zh/docs/rust/surpalaces)。 **示例** ```rust let zh = Language::ZhCN; let sp = chart.surrounded_palaces(Palace::Soul).unwrap(); println!("{} / {} / {} / {}", translate_palace(sp.target.name, zh), translate_palace(sp.opposite.name, zh), translate_palace(sp.wealth.name, zh), translate_palace(sp.career.name, zh)); println!("三方四正见紫微: {}", sp.have(&[StarKey::ZiweiMaj])); ``` **输出** ```text 命宫 / 迁移 / 财帛 / 官禄 三方四正见紫微: true ``` *** ## is\_surrounded / is\_surrounded\_one\_of / not\_surrounded [#is_surrounded--is_surrounded_one_of--not_surrounded] **用途** 直接在星盘上判断某宫的三方四正里有没有指定星耀,省去先取三方四正的一步。 **签名** ```rust pub fn is_surrounded(&self, target: impl Into, stars: &[StarKey]) -> bool pub fn is_surrounded_one_of(&self, target: impl Into, stars: &[StarKey]) -> bool pub fn not_surrounded(&self, target: impl Into, stars: &[StarKey]) -> bool ``` **参数** | 参数 | 类型 | 必填 | 默认 | 说明 | | -------- | ------------------------- | -- | -- | -------------- | | `target` | `impl Into` | 是 | — | 定位方式同 `palace` | | `stars` | `&[StarKey]` | 是 | — | 星耀标识列表 | **返回值** | 方法 | 语义 | | ---------------------- | ----------------- | | `is_surrounded` | 列表中**每一颗**都在三方四正里 | | `is_surrounded_one_of` | 列表中**至少一颗**在三方四正里 | | `not_surrounded` | 列表中**一颗都不在**三方四正里 | **示例** ```rust use x_iztro::StarKey::*; println!("{}", chart.is_surrounded(Palace::Soul, &[ZiweiMaj, TianxiangMaj])); println!("{}", chart.is_surrounded_one_of(Palace::Soul, &[QishaMaj, PojunMaj])); println!("{}", chart.not_surrounded(Palace::Soul, &[HuoxingMin])); ``` **输出** ```text true false true ``` 命宫只坐紫微,天相在三方之一的财帛宫,因此第一行为 `true`; 七杀与破军都不在这四宫内,第二行为 `false`。 **边界与陷阱** `stars` 传空切片时,`is_surrounded` 与 `not_surrounded` 返回 `true` (「所有元素都满足」与「没有元素不满足」对空集都成立), `is_surrounded_one_of` 返回 `false`。调用前先确认列表非空。 *** ## horoscope / horoscope\_now [#horoscope--horoscope_now] **用途** 以本盘为起点计算目标日期的运限。 **签名** ```rust pub fn horoscope(&self, target_date: &str, target_time_index: u8) -> Result, IztroError> pub fn horoscope_now(&self) -> Result, IztroError> ``` **参数** | 参数 | 类型 | 必填 | 默认 | 说明 | | ------------------- | ------ | -- | -- | -------------------- | | `target_date` | `&str` | 是 | — | 目标公历日期,格式 `YYYY-M-D` | | `target_time_index` | `u8` | 是 | — | 目标时辰索引 0–12,决定流时 | `horoscope_now` 取本地时钟的当前日期与当前时辰,无参数。 **返回值** `HoroscopeRef<'_>`——持有本盘的运限视图,六个层级的宫位查询不必再传星盘。 详见[运限对象](/zh/docs/rust/horoscope)。 **示例** ```rust let zh = Language::ZhCN; let h = chart.horoscope("2025-6-1", 0)?; println!("大限 {}{}", translate_heavenly_stem(h.decadal.heavenly_stem, zh), translate_earthly_branch(h.decadal.earthly_branch, zh)); println!("流年 {}{}", translate_heavenly_stem(h.yearly.heavenly_stem, zh), translate_earthly_branch(h.yearly.earthly_branch, zh)); ``` `decadal` / `monthly` / `daily` / `hourly` 是 `HoroscopeItem`,干支直接读; `yearly` 与 `age` 各自多带一项自己的数据(通用字段收在 `base` 里), 但两者都实现了 `Deref`,`h.yearly.heavenly_stem` 同样直接可读。 **输出** ```text 大限 庚辰 流年 乙巳 ``` *** ## to\_dto [#to_dto] **用途** 把星盘转成与 JS iztro 字段契约一致的序列化结构。 **签名** ```rust pub fn to_dto(&self) -> AstrolabeDto ``` **返回值** `x_iztro::dto::AstrolabeDto`——camelCase 键、值按**排盘语言**翻译, 另带 `*Key` 语言无关标识与排盘上下文(`genderKey` / `timeIndex` / `fixLeap` / `language` / `config`)。 字段清单见[数据结构](/zh/docs/guide/data-model)。 **示例** ```rust let dto = chart.to_dto(); let json = serde_json::to_string(&dto)?; let v: serde_json::Value = serde_json::from_str(&json)?; println!("{} {}", v["solarDate"], v["palaces"][4]["nameKey"]); println!("{}", v["config"]["yearDivide"]); ``` **输出** ```text "2000-8-16" "soulPalace" "normal" ``` **边界与陷阱** DTO 是给跨语言绑定与前端用的。Rust 侧做分析请直接用 `Astrolabe`—— 它有全部查询方法,DTO 只有数据。想一步拿到 JSON 字符串用 [`by_solar_json`](/zh/docs/rust/astro#by_solar_json--by_lunar_json)。 `Config` 的 `overrides`(自定义四化与亮度表)不进 DTO:它是排盘输入而非结果, 回显会破坏与 JS iztro 的字段契约。 # 宫位对象 (/zh/docs/rust/palace) PalaceData 的字段,以及星耀判断、空宫判断与飞星族的全部方法。 宫位是斗数分析的主战场。数据本身是 `PalaceData`,`chart.palace(...)` 返回的是 `PalaceRef`——同一份数据外加一个指回星盘的引用。 | | `PalaceData` | `PalaceRef<'a>` | | ---- | ------------------------------------------------------- | ------------------------------------------------------------------------- | | 从哪来 | `chart.palaces[i]`、`sp.target` 等字段 | `chart.palace(...)`、`star.palace()`、`sp` 之外的查询入口 | | 字段 | 全部 | 经 `Deref` 全部可读,`data()` 取到底层 | | 判断方法 | `has` / `is_empty` / `flies_to` 一族(目标宫要传 `&PalaceData`) | 同名方法,目标宫可直接写索引或宫名 | | 独有 | — | `opposite_palace` / `surrounded_palaces` / `mutaged_places` / `astrolabe` | 本页条目按 `PalaceRef` 的形式给签名;`PalaceData` 上的同名方法只差在飞星族的 目标宫参数类型(`&PalaceData` 而非 `impl Into`)。 ```rust let soul = chart.palace(Palace::Soul).unwrap(); soul.name; // 经 Deref 直接取字段 soul.opposite_palace(); // 视图独有 ``` 本页示例统一用 `Language::ZhCN` 排盘,因此输出里的展示值都是中文。 `name` 等枚举字段本身与语言无关,展示时才经 `translate_*` 转成文本。 ## 字段 [#字段] | 字段 | 类型 | 说明 | | -------------------- | ----------------------------- | ----------------------------- | | `index` | `usize` | 宫位索引 0–11,0 为寅宫 | | `name` | `Palace` | 宫名 | | `is_body_palace` | `bool` | 是否身宫 | | `is_original_palace` | `bool` | 是否来因宫(宫干与年干相同且不在子丑二宫) | | `heavenly_stem` | `HeavenlyStem` | 宫干,决定本宫飞出的四化 | | `earthly_branch` | `EarthlyBranch` | 宫支,由索引固定:0 为寅、11 为丑 | | `major_stars` | `Vec` | 十四主星中落在本宫的,按安放顺序 | | `minor_stars` | `Vec` | 十四辅星中落在本宫的 | | `adjective_stars` | `Vec` | 杂耀 | | `changsheng12` | `StarKey` | 长生十二神,每宫恰好一个 | | `boshi12` | `StarKey` | 博士十二神 | | `jiangqian12` | `StarKey` | 将前十二神 | | `suiqian12` | `StarKey` | 岁前十二神 | | `decadal` | `Decadal` | 大限:岁数区间与宫干支 | | `ages` | `Vec` | 小限经过本宫的虚岁列表 | | `overrides` | `Option>` | 排盘时生效的自定义四化与亮度表;未自定义时为 `None` | 主星、辅星、杂耀是**列表**,一宫可以有零到多颗。 长生、博士、将前、岁前十二神是**每宫恰好一个**的标记,十二宫刚好排满一轮, 因此是单值字段而不是列表。 `overrides` 携带的是排盘配置里的自定义表——飞星族方法要按宫干查四化, 自定义表可能改写了某个天干的四化,因此宫位得随身带着它。 它不参与序列化,DTO 与 JSON 输出里都没有这一项。 *** ## has / not\_have / has\_one\_of [#has--not_have--has_one_of] **用途** 判断本宫坐了哪些星。 **斗数含义** 星耀落宫是斗数的基本盘面信息。「命宫坐紫微天相」即 `has(&[ZiweiMaj, TianxiangMaj])`。查找范围覆盖主星、辅星、杂耀三组。 **签名** ```rust pub fn has(&self, stars: &[StarKey]) -> bool pub fn not_have(&self, stars: &[StarKey]) -> bool pub fn has_one_of(&self, stars: &[StarKey]) -> bool ``` **参数** | 参数 | 类型 | 必填 | 默认 | 说明 | | ------- | ------------ | -- | -- | ------ | | `stars` | `&[StarKey]` | 是 | — | 星耀标识列表 | **返回值** | 方法 | 语义 | | ------------ | ---------- | | `has` | 列表中每一颗都在本宫 | | `not_have` | 列表中一颗都不在本宫 | | `has_one_of` | 列表中至少一颗在本宫 | **示例** ```rust use x_iztro::StarKey::*; let soul = chart.palace(Palace::Soul).unwrap(); println!("{}", soul.has(&[ZiweiMaj, TianxiangMaj])); println!("{}", soul.has_one_of(&[QishaMaj, ZiweiMaj])); println!("{}", soul.not_have(&[HuoxingMin, LingxingMin])); ``` **输出** ```text false true true ``` 这张盘的命宫只坐紫微,天相落在财帛宫,因此要求两颗都在的 `has` 为 `false`。 **边界与陷阱** 空列表下 `has` 与 `not_have` 返回 `true`,`has_one_of` 返回 `false`。 *** ## has\_mutagen / not\_have\_mutagen [#has_mutagen--not_have_mutagen] **用途** 判断本宫有没有某种四化。 **斗数含义** 本命四化由**生年干**决定,标记打在对应的星上。 一宫「有化禄」意味着这宫里坐着的某颗星被生年干化了禄。 注意这与飞星不同——飞星看的是宫干,本处看的是星上已有的标记。 **签名** ```rust pub fn has_mutagen(&self, mutagen: Mutagen) -> bool pub fn not_have_mutagen(&self, mutagen: Mutagen) -> bool ``` **参数** | 参数 | 类型 | 必填 | 默认 | 说明 | | --------- | --------- | -- | -- | ------------------------------ | | `mutagen` | `Mutagen` | 是 | — | `Lu` / `Quan` / `Ke` / `Ji` 之一 | **返回值** `bool`。只扫描 `major_stars` 与 `minor_stars`,**不看杂耀**。 **示例** ```rust let children = chart.palace(Palace::Children).unwrap(); println!("子女宫有化禄: {}", children.has_mutagen(Mutagen::Lu)); println!("子女宫无化忌: {}", children.not_have_mutagen(Mutagen::Ji)); ``` **输出** ```text 子女宫有化禄: true 子女宫无化忌: true ``` **边界与陷阱** `has_mutagen` 只看主星与辅星上的四化标记,杂耀即使带标记也不计入 (复刻 iztro 的行为)。要连杂耀一起看,自己遍历 `adjective_stars` 的 `mutagen` 字段。 生年四化只会落在十四主星与部分辅星上,因此实际盘面上两种口径通常没有差别。 *** ## is\_empty / is\_empty\_excluding [#is_empty--is_empty_excluding] **用途** 判断本宫是否空宫。 **斗数含义** 「空宫」指没有十四主星坐守的宫。空宫要借对宫主星来看, 是斗数里一个很常见的判断分支。辅星与杂耀默认不影响空宫的成立。 **签名** ```rust pub fn is_empty(&self) -> bool pub fn is_empty_excluding(&self, exclude_stars: &[StarKey]) -> bool ``` **参数** | 参数 | 类型 | 必填 | 默认 | 说明 | | --------------- | ------------ | -- | -- | ---------------------------------- | | `exclude_stars` | `&[StarKey]` | 是 | — | 追加计入的星耀:本宫无主星、但坐了其中任一颗时,同样**不算**空宫 | **返回值** `bool`。判定顺序是:先看有无主星,有则不空;再看 `exclude_stars`,命中则不空;都不满足才是空宫。 **示例** ```rust let parents = chart.palace(Palace::Parents).unwrap(); println!("父母宫空宫: {}", parents.is_empty()); let friends = chart.palace(Palace::Friends).unwrap(); println!("仆役宫空宫: {}", friends.is_empty()); // 父母宫无主星,但坐了陀罗——把陀罗也计入后就不算空宫 println!("父母宫计入陀罗后: {}", parents.is_empty_excluding(&[StarKey::TuoluoMin])); ``` **输出** ```text 父母宫空宫: true 仆役宫空宫: false 父母宫计入陀罗后: false ``` 这张盘只有父母、田宅两宫无主星。仆役宫坐太阴,因此不算空宫。 **边界与陷阱** `exclude_stars` 不是「判断时忽略这些星」,而是「这些星也算数」。 本宫已有主星时它完全不起作用——有主星就直接不是空宫,不再看这个列表。 `is_empty` 只检查 `major_stars`。一宫辅星杂耀满座但没有主星,仍然是空宫。 要把某些辅星也当作「填实」,把它们传进 `is_empty_excluding`。 *** ## flies\_to / flies\_one\_of\_to / not\_fly\_to [#flies_to--flies_one_of_to--not_fly_to] **用途** 判断本宫宫干的四化是否飞入目标宫。 **斗数含义** 飞星派的核心手法。每个宫位有自己的宫干,宫干按四化表决定 哪四颗星化禄、权、科、忌。若被化的那颗星恰好坐在目标宫,就叫「本宫化 X 入目标宫」。 「命宫化禄入财帛」表达的是命宫这件事的顺遂落在财帛上。 **签名** ```rust pub fn flies_to(&self, target: impl Into, mutagens: &[Mutagen]) -> bool pub fn flies_one_of_to(&self, target: impl Into, mutagens: &[Mutagen]) -> bool pub fn not_fly_to(&self, target: impl Into, mutagens: &[Mutagen]) -> bool ``` **参数** | 参数 | 类型 | 必填 | 默认 | 说明 | | ---------- | ------------------------- | -- | -- | -------------------------- | | `target` | `impl Into` | 是 | — | 目标宫,索引 / 宫名 / 身宫 / 来因宫四种写法 | | `mutagens` | `&[Mutagen]` | 是 | — | 要检查的四化 | **返回值** | 方法 | 语义 | | ----------------- | ------------------ | | `flies_to` | 列出的四化**全部**飞入目标宫 | | `flies_one_of_to` | 列出的四化**至少一个**飞入目标宫 | | `not_fly_to` | 列出的四化**一个都不**飞入目标宫 | **示例** ```rust let soul = chart.palace(Palace::Soul).unwrap(); println!("命宫化禄入财帛: {}", soul.flies_to(Palace::Wealth, &[Mutagen::Lu])); println!("命宫化禄或忌入迁移: {}", soul.flies_one_of_to(Palace::Surface, &[Mutagen::Lu, Mutagen::Ji])); println!("命宫不化权入子女: {}", soul.not_fly_to(Palace::Children, &[Mutagen::Quan])); ``` **输出** ```text 命宫化禄入财帛: false 命宫化禄或忌入迁移: false 命宫不化权入子女: true ``` **边界与陷阱** `mutagens` 传空切片时 `flies_to` 返回 `false`, `flies_one_of_to` 与 `not_fly_to` 返回 `true`。 这与「空集上全称命题为真」的直觉相反,但复刻的是 iztro 的行为: `flies_to` 先算出要找的星,一颗都没有就直接判假。传空通常是调用方的疏漏, 先确认列表非空。 目标宫写成越界索引之外的无效值时,`PalaceRef` 上的三个方法一律返回 `false`, 包括语义上「否定」的 `not_fly_to`——定位失败不等于「没飞进去」。 索引会先对 12 取模,因此写 `12`、`-1` 这类值不算定位失败。 `Config::with_mutagens` 换掉某个天干的四化表后,宫干落在该天干的宫飞出的星随之改变。 飞星族方法读的是排盘时生效的表,不是内置默认表。 目标宫写成本宫时,语义上是「自化」。此时用 `self_mutaged` 一族更直观。 *** ## self\_mutaged / self\_mutaged\_one\_of / not\_self\_mutaged [#self_mutaged--self_mutaged_one_of--not_self_mutaged] **用途** 判断本宫是否自化。 **斗数含义** 自化指本宫宫干化出的星恰好就坐在本宫。 含义上是「自己把自己的能量释放掉」,与飞入他宫的定向作用不同。 **签名** ```rust pub fn self_mutaged(&self, mutagens: &[Mutagen]) -> bool pub fn self_mutaged_one_of(&self, mutagens: &[Mutagen]) -> bool pub fn not_self_mutaged(&self, mutagens: &[Mutagen]) -> bool ``` **参数** | 参数 | 类型 | 必填 | 默认 | 说明 | | ---------- | ------------ | -- | -- | ------------------- | | `mutagens` | `&[Mutagen]` | 是 | — | 要检查的四化;传空切片表示「四化全部」 | **返回值** | 方法 | 语义 | | --------------------- | ----------------------- | | `self_mutaged` | 列出的四化全部自化 | | `self_mutaged_one_of` | 列出的四化至少一个自化;列表为空时检查全部四化 | | `not_self_mutaged` | 列出的四化一个都不自化;列表为空时检查全部四化 | **示例** ```rust let career = chart.palace(Palace::Career).unwrap(); println!("官禄宫自化禄: {}", career.self_mutaged(&[Mutagen::Lu])); println!("官禄宫自化忌: {}", career.self_mutaged(&[Mutagen::Ji])); println!("官禄宫有任一自化: {}", career.self_mutaged_one_of(&[])); println!("官禄宫无任何自化: {}", career.not_self_mutaged(&[])); ``` **输出** ```text 官禄宫自化禄: false 官禄宫自化忌: true 官禄宫有任一自化: true 官禄宫无任何自化: false ``` 官禄宫宫干为丙,丙干化忌在廉贞,而廉贞正坐官禄宫,故成自化忌。 **边界与陷阱** `self_mutaged_one_of` 与 `not_self_mutaged` 把空列表解释为「全部四化」, 而不是「空集」。`self_mutaged` 不做这层回退,空列表退化成「本宫是否包含空集」, 恒为 `true`——与 `flies_to` 的空列表判假正好相反,别把两者的直觉混用。 *** ## mutaged\_places / mutagen\_stars [#mutaged_places--mutagen_stars] **用途** 取本宫宫干化出的四颗星分别落在哪些宫,或直接取那四颗星本身。 **斗数含义** 飞星分析的全景版本:不问「有没有飞到某宫」,而是一次拿到禄权科忌四个落点。 **签名** ```rust pub fn mutaged_places(&self) -> Vec>> pub fn mutagen_stars(&self, mutagens: &[Mutagen]) -> Vec ``` **参数** | 参数 | 类型 | 必填 | 默认 | 说明 | | ---------- | ------------ | -- | -- | -------------------- | | `mutagens` | `&[Mutagen]` | 是 | — | 要取的四化位;同一四化重复传入会重复出现 | **返回值** `mutaged_places` 返回长度为 4 的 `Vec`,顺序为**禄、权、科、忌**, 某颗被化的星不在这张盘上时对应位置为 `None`。 `mutagen_stars` 返回 `Vec`,顺序与传入的四化一致。 **示例** ```rust let soul = chart.palace(Palace::Soul).unwrap(); for (m, place) in ["禄", "权", "科", "忌"].iter().zip(soul.mutaged_places()) { match place { Some(p) => println!("化{m} → {}", translate_palace(p.name, Language::ZhCN)), None => println!("化{m} → 不在盘上"), } } println!("{:?}", soul.mutagen_stars(&[Mutagen::Lu, Mutagen::Ji])); ``` **输出** ```text 化禄 → 子女 化权 → 命宫 化科 → 官禄 化忌 → 财帛 [TianliangMaj, WuquMaj] ``` 命宫宫干为壬,壬干四化为天梁化禄、紫微化权、左辅化科、武曲化忌, 四颗星分别坐在子女、命宫、官禄、财帛四宫。 `mutagen_stars` 取的是「本宫宫干化出哪几颗星」,与星耀自身的 `mutagen` 字段(生年四化)无关;后者由出生年干决定。 `PalaceRef::mutaged_places` 不收参数,恒按禄、权、科、忌四位返回长度为 4 的结果。 `PalaceData` 上的同名方法要传十二宫切片(`p.mutaged_places(&chart.palaces)`), 返回的是 `Vec>` 宫位索引而不是宫位视图。 *** ## opposite\_palace / surrounded\_palaces [#opposite_palace--surrounded_palaces] **用途** 取本宫的对宫与三方四正。 **斗数含义** 对宫是本宫 +6 的那一宫,两宫永远相对而看。 三方四正在对宫之外再加上 +4(官禄位)与 +8(财帛位)。 **签名** ```rust pub fn opposite_palace(&self) -> PalaceRef<'a> pub fn surrounded_palaces(&self) -> SurroundedPalaces<'a> ``` **返回值** `opposite_palace` 必然存在,不返回 `Option`。 `surrounded_palaces` 见[三方四正](/zh/docs/rust/surpalaces)。 **示例** ```rust let zh = Language::ZhCN; let soul = chart.palace(Palace::Soul).unwrap(); println!("{} 的对宫是 {}", translate_palace(soul.name, zh), translate_palace(soul.opposite_palace().name, zh)); println!("三方四正见煞: {}", soul.surrounded_palaces().have_one_of(&[StarKey::HuoxingMin, StarKey::LingxingMin])); ``` **输出** ```text 命宫 的对宫是 迁移 三方四正见煞: true ``` *** ## astrolabe [#astrolabe] **用途** 从宫位回到它所属的星盘。 **签名** ```rust pub fn astrolabe(&self) -> &'a Astrolabe ``` **返回值** `&Astrolabe`。视图始终持有星盘,因此不返回 `Option`。 **示例** ```rust let soul = chart.palace(Palace::Soul).unwrap(); println!("{}", translate_five_elements_class(soul.astrolabe().five_elements_class, Language::ZhCN)); ``` **输出** ```text 木三局 ``` # 星耀对象 (/zh/docs/rust/star-object) Star 的字段与亮度、四化判断,以及 StarRef 的回溯能力。 `Star` 是一颗落在某宫的星,带着它的类型、亮度与四化标记。 `chart.star(...)` 返回 `StarRef`,在 `Star` 之上多出回溯所在宫的能力。 ```rust let ziwei = chart.star(StarKey::ZiweiMaj).unwrap(); let _name = &ziwei.name; // 经 Deref 直接取字段 let _palace = ziwei.palace(); // 视图独有:回溯所在宫 ``` 本页示例统一用 `Language::ZhCN` 排盘,因此输出里的展示值都是中文。 `Star` 本身不实现 `Copy`,经 `Deref` 取字段时按引用借用(`&ziwei.name`) 或克隆,不能直接搬走。 ## 字段 [#字段] | 字段 | 类型 | 说明 | | ------------ | -------------------- | ---------------------------- | | `key` | `StarKey` | 星耀标识,与语言无关,判断时用它 | | `name` | `String` | 星名,按排盘语言翻译 | | `star_type` | `StarType` | 星耀类型,见下表 | | `scope` | `Scope` | 作用范围:本命星为 `Origin`,流耀为对应运限层级 | | `brightness` | `Option` | 亮度;没有亮度表的星耀为 `None` | | `mutagen` | `Option` | 生年四化;未被生年干化的星为 `None` | ### StarType 的八个取值 [#startype-的八个取值] | 取值 | 含义 | 典型成员 | | ----------- | ---- | ----------------- | | `Major` | 十四主星 | 紫微、天府、七杀、破军 | | `Soft` | 吉星 | 左辅、右弼、文昌、文曲、天魁、天钺 | | `Tough` | 煞星 | 擎羊、陀罗、火星、铃星、地空、地劫 | | `Adjective` | 杂耀 | 三台、八座、天刑、天姚 | | `Flower` | 桃花星 | 红鸾、天喜、咸池 | | `Helper` | 解神 | 解神 | | `Lucun` | 禄存 | 禄存 | | `Tianma` | 天马 | 天马 | 禄存与天马各自独占一类,因为它们在传统分法里既非纯吉也非纯煞,判断时常单独拎出来。 只有十四主星与文昌、文曲、火星、铃星、擎羊、陀罗这二十颗有亮度表, 其余星耀本就没有亮度概念,`brightness` 为 `None`。 *** ## with\_brightness [#with_brightness] **用途** 判断这颗星是否处于给定亮度之一。 **斗数含义** 亮度(庙旺得利平不陷)描述星耀在该宫位的强弱。 同一颗星在十二宫各有定值,庙旺则力量充分发挥,落陷则受制。 **签名** ```rust pub fn with_brightness(&self, brightness: &[Brightness]) -> bool ``` **参数** | 参数 | 类型 | 必填 | 默认 | 说明 | | ------------ | --------------- | -- | -- | ------------ | | `brightness` | `&[Brightness]` | 是 | — | 亮度列表,命中任一即为真 | **返回值** `bool`。该星无亮度时恒为 `false`。 **示例** ```rust let ziwei = chart.star(StarKey::ZiweiMaj).unwrap(); println!("{}", ziwei.with_brightness(&[Brightness::Miao])); println!("{}", ziwei.with_brightness(&[Brightness::Wang, Brightness::De])); ``` **输出** ```text true false ``` **边界与陷阱** 语义是「命中任一」而非「全部命中」——一颗星只有一个亮度,要求全部命中在列表多于一项时永假。 *** ## with\_mutagen [#with_mutagen] **用途** 判断这颗星是否带指定的生年四化。 **斗数含义** 生年四化由出生年干决定,一年固定四颗星分别化禄、权、科、忌。 这个标记跟着星走,无论那颗星落在哪一宫。 **签名** ```rust pub fn with_mutagen(&self, mutagens: &[Mutagen]) -> bool ``` **参数** | 参数 | 类型 | 必填 | 默认 | 说明 | | ---------- | ------------ | -- | -- | ------------ | | `mutagens` | `&[Mutagen]` | 是 | — | 四化列表,命中任一即为真 | **返回值** `bool`。该星未被生年干化时恒为 `false`。 **示例** ```rust let ziwei = chart.star(StarKey::ZiweiMaj).unwrap(); let taiyang = chart.star(StarKey::TaiyangMaj).unwrap(); println!("紫微化禄: {}", ziwei.with_mutagen(&[Mutagen::Lu])); println!("太阳化禄: {}", taiyang.with_mutagen(&[Mutagen::Lu])); ``` **输出** ```text 紫微化禄: false 太阳化禄: true ``` 这张盘生年干为庚,庚干太阳化禄,因此标记落在太阳而非紫微。 **边界与陷阱** `with_mutagen` 看的是**生年干**给这颗星打的标记,一张盘上只有四颗星带标记。 宫干飞出的四化不在这里体现,用宫位的 [`flies_to`](/zh/docs/rust/palace#flies_to--flies_one_of_to--not_fly_to) 一族。 *** ## palace / opposite\_palace / surrounded\_palaces [#palace--opposite_palace--surrounded_palaces] **用途** 从星回溯到它所在的宫、该宫的对宫与三方四正。 **签名** ```rust pub fn palace(&self) -> PalaceRef<'a> pub fn opposite_palace(&self) -> PalaceRef<'a> pub fn surrounded_palaces(&self) -> SurroundedPalaces<'a> ``` **返回值** 三者都必然存在,不返回 `Option`——`StarRef` 只能由星盘查询产生,天然持有归属关系。 **示例** ```rust let zh = Language::ZhCN; let ziwei = chart.star(StarKey::ZiweiMaj).unwrap(); println!("{}", translate_palace(ziwei.palace().name, zh)); println!("{}", translate_palace(ziwei.opposite_palace().name, zh)); println!("同宫或三方见天相: {}", ziwei.surrounded_palaces().have(&[StarKey::TianxiangMaj])); ``` **输出** ```text 命宫 迁移 同宫或三方见天相: true ``` **边界与陷阱** 一颗星在一张盘上只出现一次,因此 `chart.star(key)` 的结果唯一。 运限流耀不在本命盘的星耀列表里,要取它们用运限对象的 [`palace`](/zh/docs/rust/horoscope#palace) 配合层级参数。 # 三方四正 (/zh/docs/rust/surpalaces) SurroundedPalaces 的四个宫位与五个判断方法。 三方四正是斗数最常用的取象范围。看一件事不能只看本宫, 对宫与两个三合宫的星耀同样作用其上,四宫合看才完整。 本页示例统一用 `Language::ZhCN` 排盘,因此输出里的展示值都是中文。 ## 四个宫位 [#四个宫位] | 字段 | 相对本宫 | 传统称呼 | 意义 | | ---------- | ---- | ---- | -------------- | | `target` | +0 | 本宫 | 事情本身 | | `opposite` | +6 | 对宫 | 与本宫相对的一面,影响最直接 | | `career` | +4 | 官禄位 | 三合之一 | | `wealth` | +8 | 财帛位 | 三合之一 | 四个字段的类型都是 `&'a PalaceData`(不是 `PalaceRef`): 字段可以直接读,[宫位对象](/zh/docs/rust/palace)上那些不需要星盘上下文的方法 (`has`、`is_empty`、`has_mutagen`、`mutagen_stars`)也都能调; 要用 `opposite_palace`、`surrounded_palaces` 这类需要回溯星盘的方法, 拿 `sp.target.index` 再走 `chart.palace(...)` 换成 `PalaceRef`。 `SurroundedPalaces` 声明时 `wealth` 写在 `career` 前面,但偏移是 `career = +4`、`wealth = +8`。以命宫起算时 +4 落在官禄宫、+8 落在财帛宫, 名字与偏移是这样对上的。 `wealth` 与 `career` 指的是「相对本宫的三合位置」,不是十二宫里那两个固定的宫名。 以命宫起算时它们恰好落在财帛宫与官禄宫;以别的宫起算则是别的宫。 ## 三种取法 [#三种取法] ```rust // 从星盘取 let sp = chart.surrounded_palaces(Palace::Soul).unwrap(); // 从宫位取 let sp = chart.palace(Palace::Soul).unwrap().surrounded_palaces(); // 从星耀取(该星所在宫的三方四正) let sp = chart.star(StarKey::ZiweiMaj).unwrap().surrounded_palaces(); ``` 三者结果相同,选哪个取决于手上已有什么。 *** ## have / not\_have / have\_one\_of [#have--not_have--have_one_of] **用途** 判断四宫合起来有没有指定星耀。 **斗数含义** 「三方四正见紫微」这类说法,问的正是这四宫里出没出现某颗星, 而不问具体落在其中哪一宫。 **签名** ```rust pub fn have(&self, stars: &[StarKey]) -> bool pub fn not_have(&self, stars: &[StarKey]) -> bool pub fn have_one_of(&self, stars: &[StarKey]) -> bool ``` **参数** | 参数 | 类型 | 必填 | 默认 | 说明 | | ------- | ------------ | -- | -- | ------ | | `stars` | `&[StarKey]` | 是 | — | 星耀标识列表 | **返回值** | 方法 | 语义 | | ------------- | -------------------- | | `have` | 列表中每一颗都出现在这四宫(不要求同宫) | | `not_have` | 列表中一颗都没出现 | | `have_one_of` | 列表中至少一颗出现 | **示例** ```rust use x_iztro::StarKey::*; let sp = chart.surrounded_palaces(Palace::Soul).unwrap(); println!("{}", sp.have(&[ZiweiMaj, TianxiangMaj])); println!("{}", sp.have_one_of(&[QishaMaj, PojunMaj])); println!("{}", sp.not_have(&[HuoxingMin])); ``` **输出** ```text true false true ``` 紫微在命宫、天相在财帛宫,分处两宫但都在这四宫内,因此 `have` 为 `true`。 **边界与陷阱** `have(&[A, B])` 的语义是「A 和 B 都出现在这四宫里」, 不要求它们坐在同一宫。要判断同宫,用宫位的 [`has`](/zh/docs/rust/palace#has--not_have--has_one_of)。 `have` 与 `not_have` 在空列表下返回 `true`,`have_one_of` 返回 `false`。 *** ## have\_mutagen / not\_have\_mutagen [#have_mutagen--not_have_mutagen] **用途** 判断四宫里有没有某种生年四化。 **斗数含义** 「三方四正见忌」意味着这组宫位里坐着一颗被生年干化忌的星, 是判断压力来源的常用条件。 **签名** ```rust pub fn have_mutagen(&self, mutagen: Mutagen) -> bool pub fn not_have_mutagen(&self, mutagen: Mutagen) -> bool ``` **参数** | 参数 | 类型 | 必填 | 默认 | 说明 | | --------- | --------- | -- | -- | ------------------------------ | | `mutagen` | `Mutagen` | 是 | — | `Lu` / `Quan` / `Ke` / `Ji` 之一 | **返回值** `bool`。 **示例** ```rust let sp = chart.surrounded_palaces(Palace::Soul).unwrap(); println!("三方四正见禄: {}", sp.have_mutagen(Mutagen::Lu)); println!("三方四正见忌: {}", sp.have_mutagen(Mutagen::Ji)); println!("三方四正不见科: {}", sp.not_have_mutagen(Mutagen::Ke)); ``` **输出** ```text 三方四正见禄: false 三方四正见忌: false 三方四正不见科: true ``` 这张盘的生年四化落在四宫:太阳化禄在子女、武曲化权在财帛、太阴化科在仆役、天同化忌在疾厄。 命宫的三方四正是命宫、迁移、财帛、官禄——只有化权那一颗落在里面, 因此查禄、查忌都是 `false`,查权则会是 `true`。 **边界与陷阱** 这里看的是**生年四化**打在星上的标记,与宫干飞出的四化无关。 后者请用宫位的飞星族方法。 # 运限对象 (/zh/docs/rust/horoscope) 六个运限层级的数据结构,以及不必再传星盘的宫位查询方法。 运限把本命盘投影到某个时间点上。同一张盘,不同年份看到的宫位分布不同—— 这正是「大限走到哪一宫」的意思。 ```rust let h = chart.horoscope("2025-6-1", 0)?; ``` `HoroscopeRef` 持有发起它的那张本命盘,因此所有查询方法都不必再把星盘传进去。 本页示例统一用 `Language::ZhCN` 的本命盘,因此输出里的展示值都是中文。 ## HoroscopeData [#horoscopedata] `HoroscopeRef` 经 `Deref` 得到 `HoroscopeData`,它有八个字段:两个日期串与六个层级。 | 字段 | 类型 | 说明 | | ------------ | --------------- | ------------ | | `solar_date` | `String` | 目标公历日期,与入参一致 | | `lunar_date` | `String` | 目标日期的农历中文写法 | | `decadal` | `HoroscopeItem` | 大限 | | `age` | `AgeItem` | 小限 | | `yearly` | `YearlyItem` | 流年 | | `monthly` | `HoroscopeItem` | 流月 | | `daily` | `HoroscopeItem` | 流日 | | `hourly` | `HoroscopeItem` | 流时 | `solar_date` 是**目标日期**不是出生日期;出生日期在本命盘上,用 `h.astrolabe().solar_date` 取。 ## 六个层级 [#六个层级] | 字段 | 类型 | 跨度 | 说明 | | --------- | --------------- | --- | ------------- | | `decadal` | `HoroscopeItem` | 十年 | 大限。未起运的幼年期为童限 | | `age` | `AgeItem` | 一年 | 小限。按虚岁逐年走一宫 | | `yearly` | `YearlyItem` | 一年 | 流年。按流年干支定宫 | | `monthly` | `HoroscopeItem` | 一月 | 流月 | | `daily` | `HoroscopeItem` | 一日 | 流日 | | `hourly` | `HoroscopeItem` | 一时辰 | 流时 | 两者都是一年一走,但起法不同:小限从生年地支起、按虚岁顺推, 流年直接看那一年的干支落在哪一宫。两条线互相独立,斗数里通常并看。 ### HoroscopeItem [#horoscopeitem] | 字段 | 类型 | 说明 | | ---------------- | ------------------------ | ------------------------- | | `index` | `usize` | 该层级落在哪一宫(宫位索引) | | `name` | `String` | 层级显示名,按输出语言翻译 | | `heavenly_stem` | `HeavenlyStem` | 该层级的天干,决定它飞出的四化 | | `earthly_branch` | `EarthlyBranch` | 该层级的地支 | | `palace_names` | `Vec` | 以该层级所在宫为命宫重推的十二宫名,按宫位索引排列 | | `mutagen` | `Vec` | 该层级天干引发的四化星,顺序为禄权科忌 | | `stars` | `Option>>` | 该层级的流耀分布;无流耀的层级为 `None` | `age` 与 `yearly` 不是 `HoroscopeItem` 本身,而是各自多带一项数据的包装: ```rust pub struct AgeItem { pub base: HoroscopeItem, pub nominal_age: u32, // 该日期对应的虚岁 } pub struct YearlyItem { pub base: HoroscopeItem, pub yearly_dec_star: YearlyDecStar, } pub struct YearlyDecStar { pub jiangqian12: Vec, // 流年将前十二神,按宫位索引排列 pub suiqian12: Vec, // 流年岁前十二神,按宫位索引排列 } ``` `AgeItem` 与 `YearlyItem` 都实现 `Deref`,通用字段直接读: `h.yearly.heavenly_stem`、`h.age.index`;需要整个 `HoroscopeItem` 时取 `.base`。 四个类型都在 crate 根重导出。 **示例** ```rust let h = chart.horoscope("2025-6-1", 0)?; for item in [&h.decadal, &h.monthly, &h.daily, &h.hourly] { println!("{} 落在宫位 {} 干支 {}{}", item.name, item.index, translate_heavenly_stem(item.heavenly_stem, Language::ZhCN), translate_earthly_branch(item.earthly_branch, Language::ZhCN)); } println!("小限虚岁 {}", h.age.nominal_age); ``` **输出** ```text 大限 落在宫位 2 干支 庚辰 流月 落在宫位 3 干支 壬午 流日 落在宫位 8 干支 辛丑 流时 落在宫位 8 干支 戊子 小限虚岁 26 ``` *** ## age\_palace [#age_palace] **用途** 取小限当年所在的宫。 **斗数含义** 小限是逐年推移的一条线,落在哪一宫就以那宫为该年重点。 **签名** ```rust pub fn age_palace(&self) -> PalaceRef<'a> ``` **返回值** `PalaceRef`——本命盘上的宫位,必然存在。 **示例** ```rust let h = chart.horoscope("2025-6-1", 0)?; println!("{}", translate_palace(h.age_palace().name, Language::ZhCN)); ``` **输出** ```text 田宅 ``` *** ## palace [#palace] **用途** 取某个运限层级下、按该层级重推的十二宫中的某一宫。 **斗数含义** 大限走到某宫后,以那一宫为「大限命宫」重排十二宫。 「大限的夫妻宫」问的就是这套重排后的宫位,与本命夫妻宫通常不是同一宫。 **签名** ```rust pub fn palace(&self, name: Palace, scope: Scope) -> Option> ``` **参数** | 参数 | 类型 | 必填 | 默认 | 说明 | | ------- | -------- | -- | -- | ----------- | | `name` | `Palace` | 是 | — | 要取的宫名 | | `scope` | `Scope` | 是 | — | 在哪个层级的十二宫里找 | **返回值** `Option>`——本命盘上的宫位(同一格宫位在不同层级有不同宫名)。 层级为 `Origin` 时即本命十二宫。 **示例** ```rust let zh = Language::ZhCN; let h = chart.horoscope("2025-6-1", 0)?; println!("大限命宫落在本命的 {}", translate_palace(h.palace(Palace::Soul, Scope::Decadal).unwrap().name, zh)); println!("本命命宫是 {}", translate_palace(h.palace(Palace::Soul, Scope::Origin).unwrap().name, zh)); ``` **输出** ```text 大限命宫落在本命的 夫妻 本命命宫是 命宫 ``` **边界与陷阱** `palace(Soul, Decadal)` 返回的宫位对象上,`name` 仍是**本命宫名**(例中的夫妻), 因为它就是本命盘上的那一格。要看该格在大限层级叫什么,查 `h.decadal.palace_names[index]`。 *** ## surround\_palaces [#surround_palaces] **用途** 取某个运限层级下某宫的三方四正。 **签名** ```rust pub fn surround_palaces(&self, name: Palace, scope: Scope) -> Option> ``` **参数** 同 `palace`。 **返回值** `Option>`,判断方法见[三方四正](/zh/docs/rust/surpalaces)。 **示例** ```rust let h = chart.horoscope("2025-6-1", 0)?; let sp = h.surround_palaces(Palace::Wealth, Scope::Yearly).unwrap(); println!("流年财帛的三方四正以本命 {} 为本宫", translate_palace(sp.target.name, Language::ZhCN)); ``` **输出** ```text 流年财帛的三方四正以本命 疾厄 为本宫 ``` *** ## has\_horoscope\_stars / has\_one\_of\_horoscope\_stars / not\_have\_horoscope\_stars [#has_horoscope_stars--has_one_of_horoscope_stars--not_have_horoscope_stars] **用途** 判断某层级某宫里有没有指定的流耀。 **斗数含义** 流耀是随运限层级产生的一组星:魁钺昌曲禄羊陀马鸾喜。 它们在不同层级有不同名字——大限层级叫运魁、运钺,流年层级叫流魁、流钺, 含义相同但作用于各自的时间跨度。 **签名** ```rust pub fn has_horoscope_stars(&self, name: Palace, scope: Scope, stars: &[StarKey]) -> bool pub fn has_one_of_horoscope_stars(&self, name: Palace, scope: Scope, stars: &[StarKey]) -> bool pub fn not_have_horoscope_stars(&self, name: Palace, scope: Scope, stars: &[StarKey]) -> bool ``` **参数** | 参数 | 类型 | 必填 | 默认 | 说明 | | ------- | ------------ | -- | -- | ------------- | | `name` | `Palace` | 是 | — | 该层级下的宫名 | | `scope` | `Scope` | 是 | — | 运限层级 | | `stars` | `&[StarKey]` | 是 | — | 流耀标识,须用该层级的名字 | **返回值** | 方法 | 语义 | | ---------------------------- | ----- | | `has_horoscope_stars` | 每一颗都在 | | `has_one_of_horoscope_stars` | 至少一颗在 | | `not_have_horoscope_stars` | 一颗都不在 | **示例** ```rust use x_iztro::StarKey::*; let h = chart.horoscope("2025-6-1", 0)?; println!("{}", h.has_horoscope_stars(Palace::Soul, Scope::Decadal, &[Yunlu])); println!("{}", h.has_one_of_horoscope_stars(Palace::Soul, Scope::Decadal, &[Yunlu, Yunyang])); println!("{}", h.not_have_horoscope_stars(Palace::Soul, Scope::Decadal, &[Yuntuo])); ``` **输出** ```text false false true ``` **边界与陷阱** 三个方法用 `scope` + `name` 定位到本命盘上的某一格, 但要比对的星耀集合恒为**大限流耀与流年流耀的并集**,与 `scope` 无关。 因此 `scope` 传 `Monthly` 时,查的是「流月某宫这一格里有没有大限或流年的流耀」, 而不是流月自己的流耀——流月、流日、流时三层的流耀不参与这里的比对。 要按层级取流耀分布,用 `h.monthly.stars` 一类字段,或 [`get_horoscope_stars`](/zh/docs/rust/star#get_horoscope_stars)。 大限流耀叫 `Yunlu`(运禄)、`Yunyang`(运羊)……,流年流耀叫 `Liulu`(流禄)、 `Liuyang`(流羊)……,两组标识不同名。由于比对集合恒是这两组的并集, `Yunlu` 与 `Liulu` 在任何 `scope` 下都查得到,只是落宫不同。 各层级的标识对照见[安星模块](/zh/docs/rust/star#get_horoscope_stars)。 `Origin` 走的是本命十二宫,因此仍能定位到宫位; 只是本命盘上没有流耀,比对的仍是大限与流年的流耀落在那一格的部分。 *** ## has\_horoscope\_mutagen [#has_horoscope_mutagen] **用途** 判断某层级某宫里有没有该层级天干引发的四化。 **斗数含义** 每个运限层级有自己的天干,会像生年干一样化出四颗星。 「大限化禄落在大限财帛」这类判断问的就是这个。 **签名** ```rust pub fn has_horoscope_mutagen(&self, name: Palace, scope: Scope, mutagen: Mutagen) -> bool ``` **参数** | 参数 | 类型 | 必填 | 默认 | 说明 | | --------- | --------- | -- | -- | ------- | | `name` | `Palace` | 是 | — | 该层级下的宫名 | | `scope` | `Scope` | 是 | — | 运限层级 | | `mutagen` | `Mutagen` | 是 | — | 四化之一 | **返回值** `bool`。检查该层级天干化出的那颗星是否落在目标宫的主星或辅星里(不看杂耀)。 **示例** ```rust let h = chart.horoscope("2025-6-1", 0)?; println!("{}", h.has_horoscope_mutagen(Palace::Soul, Scope::Decadal, Mutagen::Lu)); // 该层级化出的四颗星本身可直接读 println!("{:?}", h.decadal.mutagen.iter() .map(|s| translate_star(*s, Language::ZhCN)).collect::>()); ``` **输出** ```text false ["太阳", "武曲", "太阴", "天同"] ``` 大限干为庚,庚干四化为太阳化禄、武曲化权、太阴化科、天同化忌。 **边界与陷阱** 本命层级没有「层级天干」这回事——生年四化已经打在星耀自身的 `mutagen` 字段上。 `has_horoscope_mutagen(name, Scope::Origin, m)` 因此直接返回 `false`, 不代表本命盘上没有这个四化。要查本命四化,用宫位的 [`has_mutagen`](/zh/docs/rust/palace#has_mutagen--not_have_mutagen)。 *** ## astrolabe / data / into\_data [#astrolabe--data--into_data] **用途** 回到本命盘,或取出运限的纯数据。 **签名** ```rust pub fn astrolabe(&self) -> &'a Astrolabe pub fn data(&self) -> &HoroscopeData pub fn into_data(self) -> HoroscopeData ``` **返回值** | 方法 | 用途 | | ----------- | ----------------------------------------- | | `astrolabe` | 回到发起这次运限的本命盘 | | `data` | 借用底层数据;视图已实现 `Deref`,通常直接写 `h.decadal` 即可 | | `into_data` | 取走数据、丢掉对星盘的借用,用于需要 `'static` 生命周期的场合 | **示例** ```rust let h = chart.horoscope("2025-6-1", 0)?; println!("{}", h.astrolabe().solar_date); let data: HoroscopeData = h.into_data(); // 不再借用 chart println!("{}", data.solar_date); ``` **输出** ```text 2000-8-16 2025-6-1 ``` *** ## to\_dto [#to_dto] **用途** 把运限数据转成与 JS iztro 字段契约一致的序列化结构。 **签名** ```rust pub fn to_dto(&self, lang: Language) -> HoroscopeDto ``` **参数** | 参数 | 类型 | 必填 | 默认 | 说明 | | ------ | ---------- | -- | -- | --------- | | `lang` | `Language` | 是 | — | 译名字段用哪种语言 | 定义在 `HoroscopeData` 上(不是 `HoroscopeRef`)。运限数据本身不记语言, 因此这里要显式传——通常传 `chart.language` 与本命盘保持一致。 **返回值** `x_iztro::dto::HoroscopeDto`,camelCase 键 + `*Key` 标识。 **示例** ```rust let h = chart.horoscope("2025-6-1", 0)?; let json = serde_json::to_string(&h.to_dto(chart.language))?; let v: serde_json::Value = serde_json::from_str(&json)?; println!("{} {}", v["solarDate"], v["decadal"]["heavenlyStem"]); println!("{}", v["age"]["nominalAge"]); ``` **输出** ```text "2025-6-1" "庚" 26 ``` # 轻量查询 (/zh/docs/rust/query) 不排整盘就能拿到的生肖、星座与命宫主星。 有些问题不需要整张星盘。这五个函数各自只跑到必要的那一步就返回, 结果与完整排盘的对应字段永远一致——它们走的是同一套核心逻辑。 本页示例统一用 `Language::ZhCN` 排盘,因此输出里的展示值都是中文。 *** ## get\_zodiac\_by\_solar\_date [#get_zodiac_by_solar_date] **用途** 由公历日期取生肖。 **斗数含义** 生肖由**年支**决定,而年支的换算时点受 `year_divide` 影响。 正月初一与立春之间出生的人,两种配置会得到不同的生肖——这不是 bug,是流派差异。 **签名** ```rust pub fn get_zodiac_by_solar_date( solar_date: &str, language: Language, config: Config, ) -> Result ``` **参数** | 参数 | 类型 | 必填 | 默认 | 说明 | | ------------ | ---------- | -- | -- | -------------------- | | `solar_date` | `&str` | 是 | — | 公历日期,格式 `YYYY-M-D` | | `language` | `Language` | 是 | — | 输出语言 | | `config` | `Config` | 是 | — | 仅 `year_divide` 影响结果 | **返回值** `String`——按语言翻译的生肖名。 **示例** ```rust println!("{}", get_zodiac_by_solar_date("2000-8-16", Language::ZhCN, Config::default())?); ``` **输出** ```text 龙 ``` **边界与陷阱** 默认按正月初一换年。改成 `YearDivide::Exact` 后按立春换年, 1 月下旬到 2 月上旬出生的人可能拿到不同生肖。 *** ## get\_sign\_by\_solar\_date / get\_sign\_by\_lunar\_date [#get_sign_by_solar_date--get_sign_by_lunar_date] **用途** 取星座。 **斗数含义** 星座是西洋占星概念,只由公历日期决定,与斗数算法无关。 农历版本先把农历转成公历再判定,因此两者对同一天的结果相同。 **签名** ```rust pub fn get_sign_by_solar_date(solar_date: &str, language: Language) -> Result pub fn get_sign_by_lunar_date( lunar_date: &str, is_leap_month: bool, language: Language, ) -> Result ``` **参数** | 参数 | 类型 | 必填 | 默认 | 说明 | | --------------------------- | ---------- | -- | -- | ---------------- | | `solar_date` / `lunar_date` | `&str` | 是 | — | 日期,格式 `YYYY-M-D` | | `is_leap_month` | `bool` | 是 | — | 仅农历版本:该月是否闰月 | | `language` | `Language` | 是 | — | 输出语言 | 无 `config` 参数——星座不受任何配置影响。 **返回值** `String`。 **示例** ```rust println!("{}", get_sign_by_solar_date("2000-8-16", Language::ZhCN)?); println!("{}", get_sign_by_lunar_date("2000-7-17", false, Language::ZhCN)?); ``` **输出** ```text 狮子座 狮子座 ``` *** ## get\_major\_star\_by\_solar\_date / get\_major\_star\_by\_lunar\_date [#get_major_star_by_solar_date--get_major_star_by_lunar_date] **用途** 只取命宫主星,不排整盘。 **斗数含义** 命宫主星是斗数最常被单独问起的一项。 命宫为空宫时按惯例借对宫主星来看,本函数已经处理了这一步。 **签名** ```rust pub fn get_major_star_by_solar_date( solar_date: &str, time_index: u8, fix_leap: bool, language: Language, config: Config, ) -> Result pub fn get_major_star_by_lunar_date( lunar_date: &str, time_index: u8, leap: LeapMonth, language: Language, config: Config, ) -> Result ``` **参数** | 参数 | 类型 | 必填 | 默认 | 说明 | | --------------------------- | ----------- | -- | -- | ----------------------------------------------------------------------------------- | | `solar_date` / `lunar_date` | `&str` | 是 | — | 日期 | | `time_index` | `u8` | 是 | — | 时辰索引 0–12,命宫由月份与时辰共同决定 | | `fix_leap` | `bool` | 是 | — | 仅阳历版本:阳历日期落在闰月十五之后时是否视作次月 | | `leap` | `LeapMonth` | 是 | — | 仅农历版本:`NotLeap` / `Leap` / `LeapFixed`,见 [`by_lunar`](/zh/docs/rust/astro#by_lunar) | | `language` | `Language` | 是 | — | 输出语言 | | `config` | `Config` | 是 | — | 排盘配置 | **返回值** `String`——多颗主星以逗号分隔;空宫时返回对宫主星。 **示例** ```rust let cfg = Config::default(); println!("{}", get_major_star_by_solar_date("2000-8-16", 2, true, Language::ZhCN, cfg.clone())?); println!("{}", get_major_star_by_solar_date("2000-8-16", 2, true, Language::EnUS, cfg.clone())?); ``` **输出** ```text 紫微 emperor ``` **边界与陷阱** 命宫由农历月份与出生时辰共同定位,因此 `time_index` 是必填的。 只知道日期不知道时辰时,斗数无法给出确定的命宫。 返回值是翻译后的字符串,换语言就会变。要做程序判断请排整盘, 用 `chart.palace(Palace::Soul)` 取宫位后比较 `major_stars` 里的 `key`。 # 工具函数 (/zh/docs/rust/util) 索引换算、亮度与四化查表、命身宫推算、四柱展示串。 这些函数是排盘算法的零件。自己实现斗数逻辑、或要复核某一步推算时用得上; 日常排盘不必直接调用。 参数与返回值中的枚举都与语言无关,可直接与星盘上的字段互操作。 *** ## fix\_index [#fix_index] **用途** 把任意整数约束到 `0..max` 的循环区间(含 0,不含 `max`)。 **斗数含义** 十二宫首尾相接,从丑宫(索引 11)再走一格回到寅宫(索引 0)。 所有「顺数几格、逆数几格」的推算都靠这个回绕。 **签名** ```rust pub fn fix_index(index: i32, max: i32) -> usize ``` **参数** | 参数 | 类型 | 必填 | 默认 | 说明 | | ------- | ----- | -- | -- | ------------------ | | `index` | `i32` | 是 | — | 待修正的索引,可为负 | | `max` | `i32` | 是 | — | 循环长度,宫位用 12、天干用 10 | **返回值** `usize`,落在 `0..max`(`max` 本身不会出现)。`max` 传 0 会触发除零 panic, 调用方自己保证它是正数——盘上的用法固定为 12 或 10。 **示例** ```rust println!("{} {}", utils::fix_index(-1, 12), utils::fix_index(13, 12)); ``` **输出** ```text 11 1 ``` **边界与陷阱** 负数按数学取模回绕(-1 → 11),不是截断到 0。 *** ## earthly\_branch\_to\_palace\_index [#earthly_branch_to_palace_index] **用途** 地支转宫位索引。 **斗数含义** 十二宫的排列从**寅宫**起,而地支的自然顺序从**子**起,两者差两格。 这个函数负责这层换算:寅 → 0,卯 → 1,⋯,子 → 10,丑 → 11。 **签名** ```rust pub fn earthly_branch_to_palace_index(branch: EarthlyBranch) -> usize ``` **参数** | 参数 | 类型 | 必填 | 默认 | 说明 | | -------- | --------------- | -- | -- | -- | | `branch` | `EarthlyBranch` | 是 | — | 地支 | **返回值** `usize`,0–11。 **示例** ```rust println!("寅={} 子={}", utils::earthly_branch_to_palace_index(EarthlyBranch::Yin), utils::earthly_branch_to_palace_index(EarthlyBranch::Zi)); ``` **输出** ```text 寅=0 子=10 ``` *** ## time\_to\_index [#time_to_index] **用途** 小时数转时辰索引。 **斗数含义** 一天十二时辰,每时辰两小时,但子时横跨午夜被拆成早子时(0)与晚子时(12), 因此索引有 13 个值。 **签名** ```rust pub fn time_to_index(hour: u8) -> u8 ``` **参数** | 参数 | 类型 | 必填 | 默认 | 说明 | | ------ | ---- | -- | -- | -------- | | `hour` | `u8` | 是 | — | 小时数 0–23 | **返回值** `u8`,0–12。 **示例** ```rust println!("{} {} {}", utils::time_to_index(0), utils::time_to_index(4), utils::time_to_index(23)); ``` **输出** ```text 0 2 12 ``` 0 点为早子时,4 点为寅时,23 点为晚子时。 *** ## get\_age\_index [#get_age_index] **用途** 由生年地支取小限起始宫位索引。 **斗数含义** 小限从固定的宫起,按虚岁逐年推移。起宫由生年地支所属的三合组决定: 寅午戌年起辰宫、申子辰年起戌宫、巳酉丑年起未宫、亥卯未年起丑宫。 **签名** ```rust pub fn get_age_index(branch: EarthlyBranch) -> usize ``` **返回值** `usize`,0–11。 **示例** ```rust println!("{}", utils::get_age_index(EarthlyBranch::Chen)); ``` **输出** ```text 8 ``` 辰年属申子辰组,小限从戌宫起,戌宫的索引是 8。 *** ## get\_brightness [#get_brightness] **用途** 查某颗星落在某宫时的亮度。 **签名** ```rust pub fn get_brightness(star: StarKey, palace_index: i32, config: &Config) -> Option ``` **参数** | 参数 | 类型 | 必填 | 默认 | 说明 | | -------------- | --------- | -- | -- | --------------- | | `star` | `StarKey` | 是 | — | 星耀标识 | | `palace_index` | `i32` | 是 | — | 宫位索引,越界会对 12 取模 | | `config` | `&Config` | 是 | — | 自定义亮度表会改变结果 | **返回值** `Option`。该星没有亮度表时为 `None`。 **示例** ```rust let cfg = Config::default(); println!("{:?}", utils::get_brightness(StarKey::ZiweiMaj, 4, &cfg)); println!("{:?}", utils::get_brightness(StarKey::LucunMin, 0, &cfg)); ``` **输出** ```text Some(Miao) None ``` 紫微在午宫(索引 4)庙;禄存没有亮度表。 *** ## get\_mutagen / get\_mutagens\_by\_heavenly\_stem [#get_mutagen--get_mutagens_by_heavenly_stem] **用途** 查天干四化。 **斗数含义** 十天干各自固定指派四颗星化禄、权、科、忌。 `get_mutagen` 问「这颗星在这个天干下化什么」, `get_mutagens_by_heavenly_stem` 问「这个天干化哪四颗星」。 **签名** ```rust pub fn get_mutagen(star: StarKey, stem: HeavenlyStem, config: &Config) -> Option pub fn get_mutagens_by_heavenly_stem(stem: HeavenlyStem, config: &Config) -> [StarKey; 4] ``` **参数** | 参数 | 类型 | 必填 | 默认 | 说明 | | -------- | -------------- | -- | -- | ----------- | | `star` | `StarKey` | 是 | — | 星耀标识 | | `stem` | `HeavenlyStem` | 是 | — | 天干 | | `config` | `&Config` | 是 | — | 自定义四化表会改变结果 | **返回值** `get_mutagen` 返回 `Option`,该星不在此天干的四化表内时为 `None`。 `get_mutagens_by_heavenly_stem` 返回定长四项数组,顺序为**禄、权、科、忌**。 **示例** ```rust let cfg = Config::default(); println!("{:?}", utils::get_mutagen(StarKey::TaiyangMaj, HeavenlyStem::Geng, &cfg)); println!("{:?}", utils::get_mutagen(StarKey::ZiweiMaj, HeavenlyStem::Geng, &cfg)); println!("{:?}", utils::get_mutagens_by_heavenly_stem(HeavenlyStem::Geng, &cfg) .iter().map(|s| translate_star(*s, Language::ZhCN)).collect::>()); ``` **输出** ```text Some(Lu) None ["太阳", "武曲", "太阴", "天同"] ``` *** ## get\_soul\_and\_body [#get_soul_and_body] **用途** 由农历月索引、时辰与年干推命宫、身宫。 **斗数含义** 命宫是整张盘的起点:从寅宫起正月,顺数到生月,再从生月逆数到生时。 身宫用同样的起点但顺数生时。命宫的天干由五虎遁从年干推得。 **签名** ```rust pub fn get_soul_and_body(month_index: usize, time_index: u8, yearly_stem: HeavenlyStem) -> SoulAndBody ``` **参数** | 参数 | 类型 | 必填 | 默认 | 说明 | | ------------- | -------------- | -- | -- | ---------------------------------------- | | `month_index` | `usize` | 是 | — | 农历月索引,正月为 0;由 `fix_lunar_month_index` 求得 | | `time_index` | `u8` | 是 | — | 时辰索引 0–12 | | `yearly_stem` | `HeavenlyStem` | 是 | — | 生年天干 | **返回值** `SoulAndBody`,含 `soul_index`、`body_index`、`heavenly_stem_of_soul`、`earthly_branch_of_soul`。 **示例** ```rust let sb = get_soul_and_body(6, 2, HeavenlyStem::Geng); println!("命宫索引 {} 身宫索引 {} 命宫支 {}", sb.soul_index, sb.body_index, translate_earthly_branch(sb.earthly_branch_of_soul, Language::ZhCN)); ``` **输出** ```text 命宫索引 4 身宫索引 8 命宫支 午 ``` *** ## get\_five\_elements\_class [#get_five_elements_class] **用途** 由命宫干支推五行局。 **斗数含义** 五行局(水二、木三、金四、土五、火六)决定两件大事: 紫微星的起宫位置,以及大限的起运岁数。 **签名** ```rust pub fn get_five_elements_class(stem: HeavenlyStem, branch: EarthlyBranch) -> FiveElementsClass ``` **返回值** `FiveElementsClass`。 **示例** ```rust let c = get_five_elements_class(HeavenlyStem::Ren, EarthlyBranch::Wu); println!("{}", translate_five_elements_class(c, Language::ZhCN)); ``` **输出** ```text 木三局 ``` *** ## get\_palace\_names [#get_palace_names] **用途** 由命宫索引推十二宫名。 **斗数含义** 命宫定下后,其余十一宫按固定顺序逆时针排开: 命、兄弟、夫妻、子女、财帛、疾厄、迁移、仆役、官禄、田宅、福德、父母。 **签名** ```rust pub fn get_palace_names(soul_index: usize) -> [Palace; 12] ``` **返回值** 定长十二项数组,**按宫位索引排列**——第 `i` 项就是 `chart.palaces[i]` 的宫名。 **示例** ```rust let names = get_palace_names(4); println!("{:?}", names.iter().take(4).map(|p| translate_palace(*p, Language::ZhCN)).collect::>()); ``` **输出** ```text ["财帛", "子女", "夫妻", "兄弟"] ``` 命宫在索引 4,因此索引 0(寅宫)是财帛。 *** ## get\_decadals\_and\_ages [#get_decadals_and_ages] **用途** 由命宫索引与五行局推十二宫的大限与小限。 **斗数含义** 大限从命宫起、每宫十年,起运岁数即五行局的局数 (水二局 2 岁起、木三局 3 岁起,依此类推),顺逆由性别阴阳与年支阴阳决定; 小限从年支所属三合组定的宫起,按虚岁逐年走一宫。 **签名** ```rust pub fn get_decadals_and_ages( soul_index: usize, five_elements_class: FiveElementsClass, gender: Gender, yearly_stem: HeavenlyStem, yearly_branch: EarthlyBranch, ) -> ([Decadal; 12], [Vec; 12]) ``` **参数** | 参数 | 类型 | 必填 | 默认 | 说明 | | --------------------- | ------------------- | -- | -- | ---------------- | | `soul_index` | `usize` | 是 | — | 命宫宫位索引 | | `five_elements_class` | `FiveElementsClass` | 是 | — | 五行局,决定起运岁数与紫微起宫 | | `gender` | `Gender` | 是 | — | 性别,与年支阴阳共同决定大限顺逆 | | `yearly_stem` | `HeavenlyStem` | 是 | — | 年干 | | `yearly_branch` | `EarthlyBranch` | 是 | — | 年支,决定小限起宫 | **返回值** `([Decadal; 12], [Vec; 12])`——两个定长十二项数组,均按宫位索引排列。 `Decadal` 的字段: | 字段 | 类型 | 说明 | | ---------------- | --------------- | ---------- | | `range` | `(u32, u32)` | 大限起止虚岁,含两端 | | `heavenly_stem` | `HeavenlyStem` | 该大限的天干 | | `earthly_branch` | `EarthlyBranch` | 该大限的地支 | 第二项是每宫的小限虚岁列表。 **示例** ```rust let (decadals, ages) = astro::palace::get_decadals_and_ages( 4, FiveElementsClass::Wood3rd, Gender::Female, HeavenlyStem::Geng, EarthlyBranch::Chen, ); let d = &decadals[0]; println!("寅宫 {:?} 岁 {}{}", d.range, translate_heavenly_stem(d.heavenly_stem, Language::ZhCN), translate_earthly_branch(d.earthly_branch, Language::ZhCN)); println!("{:?}", &ages[0][..3]); ``` **输出** ```text 寅宫 (43, 52) 岁 戊寅 [9, 21, 33] ``` **边界与陷阱** 整盘排出的每个宫位上已有 `decadal` 与 `ages` 字段,内容与本函数一致。 这个函数用于不排整盘、只推大限小限的场合。 *** ## fix\_lunar\_month\_index / fix\_lunar\_day\_index [#fix_lunar_month_index--fix_lunar_day_index] **用途** 求修正后的农历月索引与日索引。 **斗数含义** 闰月归属与晚子时归属是斗数两个长期有争议的边界,这两个函数把规则落定: 闰月十六日起按下月算(可关,且晚子时不进位),晚子时的日索引属次日。 **签名** ```rust pub fn fix_lunar_month_index( lunar_month: u32, lunar_day: u32, is_leap: bool, time_index: u8, fix_leap: bool, ) -> usize pub fn fix_lunar_day_index(lunar_day: u32, time_index: u8) -> u32 ``` **参数** | 参数 | 类型 | 必填 | 默认 | 说明 | | ------------- | ------ | -- | -- | --------- | | `lunar_month` | `u32` | 是 | — | 农历月份 1–12 | | `lunar_day` | `u32` | 是 | — | 农历日 | | `is_leap` | `bool` | 是 | — | 该月是否闰月 | | `time_index` | `u8` | 是 | — | 时辰索引 | | `fix_leap` | `bool` | 是 | — | 是否启用闰月修正 | **返回值** 月索引为 0-based(正月为 0);日索引在晚子时不减一。 `fix_lunar_month_index` 进位要同时满足四个条件:`is_leap` 为真、`fix_leap` 为真、 `lunar_day` 大于 15、且 `time_index` 不是 12。四者缺一,就按本月算。 **示例** ```rust println!("{}", astro::builder::fix_lunar_month_index(7, 17, false, 2, true)); println!("{} {}", astro::builder::fix_lunar_day_index(17, 2), astro::builder::fix_lunar_day_index(17, 12)); ``` **输出** ```text 6 16 17 ``` 七月非闰月,索引为 6;十七日在寅时减一得 16,在晚子时属次日故保持 17。 *** ## translate\_chinese\_date [#translate_chinese_date] **用途** 把四柱干支拼成展示串。 **签名** ```rust pub fn translate_chinese_date( pillars: [(HeavenlyStem, EarthlyBranch); 4], lang: Language, ) -> String ``` **参数** | 参数 | 类型 | 必填 | 默认 | 说明 | | --------- | ------------------------------------ | -- | -- | ------------- | | `pillars` | `[(HeavenlyStem, EarthlyBranch); 4]` | 是 | — | 四柱,顺序为年、月、日、时 | | `lang` | `Language` | 是 | — | 输出语言 | **返回值** `String`。词条均为单字符时柱内紧凑相连、柱间空格; 任一词条为多字符时柱内空格、柱间 `-`。 **示例** ```rust let pillars = [ (HeavenlyStem::Geng, EarthlyBranch::Chen), (HeavenlyStem::Jia, EarthlyBranch::Shen), (HeavenlyStem::Bing, EarthlyBranch::Wu), (HeavenlyStem::Geng, EarthlyBranch::Yin), ]; println!("{}", utils::translate_chinese_date(pillars, Language::ZhCN)); println!("{}", utils::translate_chinese_date(pillars, Language::EnUS)); ``` **输出** ```text 庚辰 甲申 丙午 庚寅 geng chen - jia shen - bing woo - geng yin ``` 星盘的 `chinese_date` 字段即由此生成,四柱枚举可从 `chart.raw_dates.chinese_date` 取。 ## merge\_stars [#merge_stars] **用途** 把多组「十二宫星耀」按宫位合并成一组。 **斗数含义** 安星是分批进行的:主星、辅星、杂耀各出一组十二宫列表。 要把它们并成一张完整盘面时用这个函数。 **签名** ```rust pub fn merge_stars(groups: &[[Vec; 12]]) -> [Vec; 12] ``` **参数** | 参数 | 类型 | 必填 | 默认 | 说明 | | -------- | -------------------- | -- | -- | ---------- | | `groups` | `&[[Vec; 12]]` | 是 | — | 若干组十二宫星耀列表 | **返回值** 合并后的十二宫列表,同宫内按传入顺序首尾相接。 **示例** ```rust use x_iztro::{star::query::{self, StarParam}, utils, Config, Gender, Language}; let param = StarParam { solar_date: "2000-8-16", time_index: 2, gender: Gender::Female, fix_leap: true, from: None, language: Language::ZhCN, config: &Config::default(), }; let major = query::get_major_stars(¶m)?; let minor = query::get_minor_stars(¶m)?; let merged = utils::merge_stars(&[major, minor]); println!("{:?}", merged[0].iter().map(|s| s.name.as_str()).collect::>()); ``` **输出** ```text ["武曲", "天相", "天马"] ``` 数组长度由类型系统保证为 12,因此不会出现 Python / Go 侧那种长度校验失败。 *** ## parse\_heavenly\_stem / parse\_earthly\_branch [#parse_heavenly_stem--parse_earthly_branch] **用途** 把中文单字的干支还原成枚举。 **斗数含义** 外部系统(八字排盘、老命书录入)常以中文单字给干支。 这两个函数是把那种输入接进来的入口。 **签名** ```rust pub fn parse_heavenly_stem(s: &str) -> Option pub fn parse_earthly_branch(s: &str) -> Option ``` **参数** | 参数 | 类型 | 必填 | 默认 | 说明 | | --- | ------ | -- | -- | ------------------ | | `s` | `&str` | 是 | — | 中文单字,如 `"甲"`、`"子"` | **返回值** `Option<...>`。只认中文单字,不认拼音、不认其他语言的译名,也不做去空白—— 不匹配返回 `None`。 **示例** ```rust use x_iztro::astro::builder::{parse_earthly_branch, parse_heavenly_stem}; println!("{:?} {:?}", parse_heavenly_stem("庚"), parse_earthly_branch("辰")); println!("{:?} {:?}", parse_heavenly_stem("geng"), parse_heavenly_stem("庚 ")); ``` **输出** ```text Some(Geng) Some(Chen) None None ``` **边界与陷阱** 这两个函数只处理中文单字。收任意语言的译名请走 [`key_of`](/zh/docs/rust/i18n#key_of),它返回 i18n key, 再用 `HeavenlyStem::from_key` / `EarthlyBranch::from_key` 转成枚举。 它们在 `x_iztro::astro::builder` 下,未在 crate 根重导出,需要写全路径。 # 安星模块 (/zh/docs/rust/star) 按出生数据取某一组星耀的落宫,以及排盘流水线的低层构件。 不排整盘、只想知道「禄存落在哪一宫」或「这张盘的杂耀怎么分布」时用这一层。 模块分两层: | 层 | 收什么 | 用途 | | ----------------------------------------------------------------- | ------ | ---------------- | | `star::query` | 出生数据 | 对外的安星入口,本页主体 | | `star::location` / `decorative` / `major` / `minor` / `adjective` | 已算好的索引 | 排盘流水线的构件,自建流程时复用 | 所有索引都是**宫位索引**:0 为寅宫,11 为丑宫。 本页示例统一用 `Language::ZhCN` 排盘,因此输出里的展示值都是中文。 ## StarParam [#starparam] `star::query` 的全部入口共用这一个参数结构。 ```rust pub struct StarParam<'a> { pub solar_date: &'a str, pub time_index: u8, pub gender: Gender, pub fix_leap: bool, pub from: Option<(HeavenlyStem, EarthlyBranch)>, pub language: Language, pub config: &'a Config, } ``` | 字段 | 类型 | 说明 | | ------------ | --------------------------------------- | ---------------------- | | `solar_date` | `&str` | 公历日期,格式 `YYYY-M-D` | | `time_index` | `u8` | 时辰索引 0–12 | | `gender` | `Gender` | 性别,决定长生与博士十二神的顺逆 | | `fix_leap` | `bool` | 是否修正闰月 | | `from` | `Option<(HeavenlyStem, EarthlyBranch)>` | 起五行局的干支;`None` 时由命宫干支起 | | `language` | `Language` | 星耀名称的输出语言 | | `config` | `&Config` | 排盘配置 | ```rust use x_iztro::star::query::StarParam; let cfg = Config::default(); let param = StarParam { solar_date: "2000-8-16", time_index: 2, gender: Gender::Female, fix_leap: true, from: None, language: Language::ZhCN, config: &cfg, }; ``` `from` 给出后,五行局改由该干支推算,进而改变紫微天府落点与长生十二神。 其余各组星的起法不受影响。用它可以取到中州派地盘、人盘的安星结果。 *** ## get\_start\_index [#get_start_index] **用途** 求紫微、天府的起始宫位。 **斗数含义** 紫微是全盘的锚点:由五行局与农历生日按「起紫微星诀」定位, 其余十三颗主星再依紫微与天府的位置铺开。天府与紫微的位置互为镜像。 **签名** ```rust pub fn get_start_index(param: &StarParam) -> Result ``` **返回值** `StartIndex { ziwei: usize, tianfu: usize }`。 **示例** ```rust let s = star::query::get_start_index(¶m)?; println!("紫微 {} 天府 {}", s.ziwei, s.tianfu); ``` **输出** ```text 紫微 4 天府 8 ``` **边界与陷阱** `from` 给出不同干支时结果随之改变——这正是中州派三张盘差异的来源。 *** ## 各组落宫索引 [#各组落宫索引] 以下六个入口形状一致:收 `&StarParam`,返回一个字段全是宫位索引的结构体。 | 函数 | 返回类型 | 字段 | 起法依据 | | -------------------------- | ------------- | ---------------------- | ----------------- | | `get_lu_yang_tuo_ma_index` | `LuYangTuoMa` | `lu` `yang` `tuo` `ma` | 年干定禄存,禄前羊后陀;天马按年支 | | `get_kui_yue_index` | `KuiYue` | `kui` `yue` | 年干 | | `get_chang_qu_index` | `ChangQu` | `chang` `qu` | 时支 | | `get_kong_jie_index` | `KongJie` | `kong` `jie` | 时支 | | `get_timely_star_index` | `TimelyStars` | `taifu` `fenggao` | 时支 | | `get_luan_xi_index` | `LuanXi` | `hongluan` `tianxi` | 年支 | **示例** ```rust use x_iztro::star::query as sq; let l = sq::get_lu_yang_tuo_ma_index(¶m)?; println!("禄存 {} 擎羊 {} 陀罗 {} 天马 {}", l.lu, l.yang, l.tuo, l.ma); let c = sq::get_chang_qu_index(¶m)?; println!("文昌 {} 文曲 {}", c.chang, c.qu); let lx = sq::get_luan_xi_index(¶m)?; println!("红鸾 {} 天喜 {}", lx.hongluan, lx.tianxi); ``` **输出** ```text 禄存 6 擎羊 7 陀罗 5 天马 0 文昌 6 文曲 4 红鸾 9 天喜 3 ``` 擎羊在禄存前一格、陀罗在后一格,这是「禄前羊刃当,禄后陀罗府」的直接体现。 *** ## get\_daily\_star\_index / get\_monthly\_star\_index / get\_yearly\_star\_index [#get_daily_star_index--get_monthly_star_index--get_yearly_star_index] **用途** 取按日、按月、按年起的杂耀落宫。 **斗数含义** 杂耀按起法分组:日系星从辅星位置起初一顺数到生日; 月系星按农历月份定位;年系星最多,按年干或年支起。 **签名** ```rust pub fn get_daily_star_index(param: &StarParam) -> Result pub fn get_monthly_star_index(param: &StarParam) -> Result pub fn get_yearly_star_index(param: &StarParam) -> Result ``` **返回值** | 类型 | 字段 | | ------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | | `DailyStar` | `santai` `bazuo` `enguang` `tiangui` | | `MonthlyStar` | `jieshen` `tianyao` `tianxing` `yinsha` `tianyue` `tianwu` | | `YearlyStars` | 29 项:`tiancai` `tianshou` `tianchu` `posui` `feilian` `longchi` `fengge` `tianku` `tianxu` `tianguan` `tianfu` `tiande` `yuede` `tiankong` `jielu` `kongwang` `xunkong` `jiekong` `tianshang` `tianshi` `huagai` `xianchi` `guchen` `guasu` `jiesha` `nianjie` `dahao` `hongluan` `tianxi` | **示例** ```rust let d = sq::get_daily_star_index(¶m)?; println!("三台 {} 八座 {} 恩光 {} 天贵 {}", d.santai, d.bazuo, d.enguang, d.tiangui); let m = sq::get_monthly_star_index(¶m)?; println!("解神 {} 天姚 {} 天刑 {}", m.jieshen, m.tianyao, m.tianxing); let y = sq::get_yearly_star_index(¶m)?; println!("咸池 {} 华盖 {} 天伤 {} 天使 {}", y.xianchi, y.huagai, y.tianshang, y.tianshi); ``` **输出** ```text 三台 0 八座 10 恩光 9 天贵 7 解神 0 天姚 5 天刑 1 咸池 7 华盖 2 天伤 9 天使 11 ``` **边界与陷阱** 年系杂耀属流年神煞,取年支时用的是 `horoscope_divide` 而非 `year_divide`。 两个配置不同时,年系星与主星、辅星可能基于不同的年支——这是刻意的流派区分。 `YearlyStars` 里带 `hongluan` / `tianxi` 两项,`get_luan_xi_index` 也单独给这两颗。 两者取值一致,区别只在 `get_luan_xi_index` 不必算其余二十七颗。 这三项只在 `algorithm` 为中州派时进入盘面,替换掉截路、空亡与大耗的默认取法; 默认派别下它们仍会被算出来,只是不安进宫位。 *** ## get\_major\_stars / get\_minor\_stars / get\_adjective\_stars [#get_major_stars--get_minor_stars--get_adjective_stars] **用途** 取主星、辅星、杂耀在十二宫的完整分布。 **签名** ```rust pub fn get_major_stars(param: &StarParam) -> Result<[Vec; 12], IztroError> pub fn get_minor_stars(param: &StarParam) -> Result<[Vec; 12], IztroError> pub fn get_adjective_stars(param: &StarParam) -> Result<[Vec; 12], IztroError> ``` **返回值** 定长十二项数组,按宫位索引排列。每项是该宫的星耀列表(可能为空)。 **示例** ```rust let major = sq::get_major_stars(¶m)?; for (i, stars) in major.iter().take(5).enumerate() { println!("[{i}] {:?}", stars.iter().map(|s| s.name.as_str()).collect::>()); } ``` **输出** ```text [0] ["武曲", "天相"] [1] ["太阳", "天梁"] [2] ["七杀"] [3] ["天机"] [4] ["紫微"] ``` **边界与陷阱** 返回的 `Star` 带亮度与生年四化标记,与整盘排出的完全一致—— 它们走的是同一段代码。要取整盘的话直接用 `by_solar` 更省事。 *** ## get\_changsheng12 / get\_boshi12 / get\_yearly12 [#get_changsheng12--get_boshi12--get_yearly12] **用途** 取四组十二神在十二宫的排列。 **斗数含义** 这四组各是十二个标记排满十二宫,每宫恰好一个: 长生十二神按五行局起、随性别与年支阴阳定顺逆; 博士十二神从禄存起、同样定顺逆; 岁前十二神从年支起顺行;将前十二神按年支三合组起。 **签名** ```rust pub fn get_changsheng12(param: &StarParam) -> Result<[StarKey; 12], IztroError> pub fn get_boshi12(param: &StarParam) -> Result<[StarKey; 12], IztroError> pub fn get_yearly12(param: &StarParam) -> Result<([StarKey; 12], [StarKey; 12]), IztroError> ``` **返回值** 定长十二项数组,按宫位索引排列。 `get_yearly12` 一次返回两组,顺序为 `(岁前十二神, 将前十二神)`。 **示例** ```rust let cs = sq::get_changsheng12(¶m)?; println!("{:?}", cs.iter().take(4).map(|s| translate_star(*s, Language::ZhCN)).collect::>()); let (suiqian, jiangqian) = sq::get_yearly12(¶m)?; println!("{:?}", suiqian.iter().take(4).map(|s| translate_star(*s, Language::ZhCN)).collect::>()); println!("{:?}", jiangqian.iter().take(4).map(|s| translate_star(*s, Language::ZhCN)).collect::>()); ``` **输出** ```text ["绝", "墓", "死", "病"] ["吊客", "病符", "岁建", "晦气"] ["岁驿", "息神", "华盖", "劫煞"] ``` *** ## get\_changsheng12\_start\_index / get\_jiangqian12\_start\_index [#get_changsheng12_start_index--get_jiangqian12_start_index] **用途** 只取两组十二神的起始宫位,不排整组。 **斗数含义** 长生起点由五行局定:水二局长生在申、木三局在亥、金四局在巳、 土五局在申、火六局在寅。将星起点由年支三合组定:寅午戌年在午、申子辰年在子、 巳酉丑年在酉、亥卯未年在卯。 **签名** ```rust pub fn get_changsheng12_start_index(five_elements_class: FiveElementsClass) -> usize pub fn get_jiangqian12_start_index(yearly_branch: EarthlyBranch) -> usize ``` **返回值** `usize`,0–11。这两个函数不需要出生数据,也不会失败。 **示例** ```rust use x_iztro::star::decorative::{get_changsheng12_start_index, get_jiangqian12_start_index}; println!("{} {}", get_changsheng12_start_index(FiveElementsClass::Water2nd), get_changsheng12_start_index(FiveElementsClass::Fire6th)); println!("{} {}", get_jiangqian12_start_index(EarthlyBranch::Zi), get_jiangqian12_start_index(EarthlyBranch::Wu)); ``` **输出** ```text 6 0 10 4 ``` 水二局长生在申(索引 6),火六局在寅(索引 0)。 *** ## get\_horoscope\_stars [#get_horoscope_stars] **用途** 取某个运限层级的流耀分布。 **斗数含义** 流耀是随运限产生的十颗星:魁钺昌曲禄羊陀马鸾喜。 它们的落宫由该层级的干支决定,名字随层级变化。流年层级额外多一颗年解。 **签名** ```rust pub fn get_horoscope_stars( stem: HeavenlyStem, branch: EarthlyBranch, scope: Scope, lang: Language, ) -> [Vec; 12] ``` **参数** | 参数 | 类型 | 必填 | 默认 | 说明 | | -------- | --------------- | -- | -- | --------- | | `stem` | `HeavenlyStem` | 是 | — | 该层级的天干 | | `branch` | `EarthlyBranch` | 是 | — | 该层级的地支 | | `scope` | `Scope` | 是 | — | 运限层级,决定星名 | | `lang` | `Language` | 是 | — | 输出语言 | **返回值** 定长十二项数组,按宫位索引排列。不会失败——入参是枚举,无非法值。 **各层级的星名对照** | 本命 | 大限 | 流年 | 流月 | 流日 | 流时 | | -- | -- | -- | -- | -- | -- | | 天魁 | 运魁 | 流魁 | 月魁 | 日魁 | 时魁 | | 天钺 | 运钺 | 流钺 | 月钺 | 日钺 | 时钺 | | 文昌 | 运昌 | 流昌 | 月昌 | 日昌 | 时昌 | | 文曲 | 运曲 | 流曲 | 月曲 | 日曲 | 时曲 | | 禄存 | 运禄 | 流禄 | 月禄 | 日禄 | 时禄 | | 擎羊 | 运羊 | 流羊 | 月羊 | 日羊 | 时羊 | | 陀罗 | 运陀 | 流陀 | 月陀 | 日陀 | 时陀 | | 天马 | 运马 | 流马 | 月马 | 日马 | 时马 | | 红鸾 | 运鸾 | 流鸾 | 月鸾 | 日鸾 | 时鸾 | | 天喜 | 运喜 | 流喜 | 月喜 | 日喜 | 时喜 | **示例** ```rust use x_iztro::astro::horoscope::get_horoscope_stars; let decadal = get_horoscope_stars(HeavenlyStem::Jia, EarthlyBranch::Zi, Scope::Decadal, Language::ZhCN); println!("{:?}", decadal.iter().take(4) .map(|g| g.iter().map(|s| s.name.as_str()).collect::>()).collect::>()); let origin = get_horoscope_stars(HeavenlyStem::Jia, EarthlyBranch::Zi, Scope::Origin, Language::ZhCN); println!("{:?}", origin.iter().take(2) .map(|g| g.iter().map(|s| s.name.as_str()).collect::>()).collect::>()); ``` **输出** ```text [["运禄", "运马"], ["运羊", "运鸾"], [], ["运昌"]] [["禄存", "天马"], ["擎羊", "红鸾"]] ``` **边界与陷阱** `Scope::Yearly` 的结果里额外含年解,按流年地支定位,安放在十颗流耀之前。 其余层级没有这一颗。 *** ## 低层构件 [#低层构件] `star::location` 与 `star::decorative` 下的函数收已算好的索引而非出生数据。 排盘流水线内部用它们,自建流程时也可复用。 ### star::location [#starlocation] | 函数 | 收 | 返回 | | ----------------------------- | ----------------------------------------------------------------------- | --------------------------------------------------------------------- | | `get_start_index` | `lunar_day, time_index, month_day_count, five_elements_value` | `StartIndex { ziwei, tianfu }` | | `get_lu_yang_tuo_ma_index` | `stem, branch` | `LuYangTuoMa { lu, yang, tuo, ma }` | | `get_kui_yue_index` | `stem` | `KuiYue { kui, yue }` | | `get_zuo_you_index` | `lunar_month` | `ZuoYou { zuo, you }` | | `get_chang_qu_index` | `time_index` | `ChangQu { chang, qu }` | | `get_chang_qu_index_by_stem` | `stem` | `ChangQu { chang, qu }`(运限层级用) | | `get_daily_star_index` | `lunar_day, time_index, zuo_index, you_index, chang_index, qu_index` | `DailyStar { santai, bazuo, enguang, tiangui }` | | `get_timely_star_index` | `time_index` | `TimelyStars { taifu, fenggao }` | | `get_kong_jie_index` | `time_index` | `KongJie { kong, jie }` | | `get_huo_ling_index` | `branch, time_index` | `HuoLing { huo, ling }` | | `get_luan_xi_index` | `branch` | `LuanXi { hongluan, tianxi }` | | `get_huagai_xianchi_index` | `branch` | `HuagaiXianchi { huagai, xianchi }` | | `get_gu_gua_index` | `branch` | `GuGua { guchen, guasu }` | | `get_jiesha_adj_index` | `branch` | `usize` | | `get_dahao_index` | `branch` | `usize` | | `get_nianjie_index` | `branch` | `usize` | | `get_tianshang_tianshi_index` | `gender, yearly_branch, soul_index, algorithm` | `(usize, usize)`,依次为天伤、天使 | | `get_tiancai_index` | `yearly_branch, soul_index` | `usize` | | `get_monthly_star_index` | `month_index` | `MonthlyStar { jieshen, tianyao, tianxing, yinsha, tianyue, tianwu }` | | `get_yearly_star_index` | `soul_index, body_index, yearly_stem, yearly_branch, gender, algorithm` | `YearlyStars`(上面那 29 项) | 所有结构体的字段都是 `usize` 宫位索引(0 为寅宫), `get_tianshang_tianshi_index` 返回的是裸元组而非具名结构体。 ### star::decorative [#stardecorative] | 函数 | 收 | 返回 | | ------------------------------ | --------------------------------- | ----------------------------------------- | | `get_changsheng12_start_index` | `five_elements_class` | `usize` | | `get_jiangqian12_start_index` | `yearly_branch` | `usize` | | `get_changsheng12` | 五行局、性别、年支等 | `[StarKey; 12]` | | `get_boshi12` | `lu_index, gender, yearly_branch` | `[StarKey; 12]` | | `get_yearly12` | 年支等 | `([StarKey; 12], [StarKey; 12])`,依次为岁前、将前 | ### star::major / minor / adjective [#starmajor--minor--adjective] `get_major_stars`、`get_minor_stars`、`get_adjective_stars`—— 与 `star::query` 下的同名函数同名不同参:这一层收已算好的索引,那一层收出生数据。 两层有若干同名函数(如两个 `get_start_index`),靠模块路径区分: `star::query::get_start_index` 收 `&StarParam`, `star::location::get_start_index` 收农历日、时辰、当月天数与五行局局数。 同时 `use` 两个模块时请用限定路径。 从出生数据到这些构件所需中间量的推算收在 `astro::context::derive`, 自建流程时先调它拿到上下文,再喂给构件即可,不必自己重推年干支与命宫。 # 数据表 (/zh/docs/rust/data) 枚举与它们的方法、排盘配置、星耀基础信息、天干地支信息与顺序常量。 `x_iztro::data` 收着两类东西:贯穿全库的**枚举**(星盘字段与几乎所有函数参数的类型), 以及排盘算法的**输入表**。两者都与输出语言无关——它们是算法的输入而非结果。 常用枚举都在 crate 根重导出,`use x_iztro::*;` 即可用。 *** ## 枚举 [#枚举] 十九个枚举 + `StarKey`。所有枚举都实现 `Debug`、`Clone`、`Copy`、`PartialEq`、`Eq` 与 serde 的 `Serialize` / `Deserialize`,可以直接比较、直接放进集合。 ### 语言无关标识:as\_key / from\_key [#语言无关标识as_key--from_key] 绝大多数枚举带一对互逆的方法,把变体与 iztro i18n key 字符串来回换。 这套 key 就是 DTO 里 `*Key` 字段的取值,也是 Python 枚举与 Go 常量的值—— 三侧写同一个字符串,判断结果一致。 | 枚举 | 变体数 | 取标识 | 由标识还原 | key 举例 | | ------------------- | --- | ---------- | ---------------- | -------------------------------- | | `StarKey` | 162 | `as_key()` | `from_key(&str)` | `ziweiMaj`、`yunlu` | | `Palace` | 12 | `as_key()` | `from_key(&str)` | `soulPalace`、`wealthPalace` | | `HeavenlyStem` | 10 | `as_key()` | `from_key(&str)` | `jiaHeavenly` | | `EarthlyBranch` | 12 | `as_key()` | `from_key(&str)` | `ziEarthly` | | `Mutagen` | 4 | `as_key()` | `from_key(&str)` | `sihuaLu` | | `Brightness` | 7 | `as_key()` | `from_key(&str)` | `miao` | | `FiveElementsClass` | 5 | `as_key()` | `from_key(&str)` | `water2nd` | | `StarType` | 8 | `as_key()` | — | `major`、`lucun` | | `Scope` | 6 | `as_key()` | `from_key(&str)` | `origin`、`decadal` | | `YearDivide` | 2 | `as_key()` | `from_key(&str)` | `normal` / `exact` | | `HoroscopeDivide` | 2 | `as_key()` | `from_key(&str)` | `normal` / `exact` | | `AgeDivide` | 2 | `as_key()` | `from_key(&str)` | `normal` / `birthday` | | `DayDivide` | 2 | `as_key()` | `from_key(&str)` | `forward` / `current` | | `Algorithm` | 2 | `as_key()` | `from_key(&str)` | `default` / `zhongzhou` | | `AstroType` | 3 | `as_key()` | `from_key(&str)` | `heaven` / `earth` / `human` | | `LeapMonth` | 3 | `as_key()` | `from_key(&str)` | `notLeap` / `leap` / `leapFixed` | `from_key` 一律返回 `Option`,未知标识给 `None`——绑定层据此拒绝非法入参。 ```rust println!("{}", StarKey::ZiweiMaj.as_key()); println!("{:?}", StarKey::from_key("taiyinMaj")); println!("{:?}", StarKey::from_key("nosuch")); println!("{} {}", Palace::Soul.as_key(), AstroType::Earth.as_key()); println!("{:?}", DayDivide::from_key("current")); ``` **输出** ```text ziweiMaj Some(TaiyinMaj) None soulPalace earth Some(Current) ``` ### 其余方法 [#其余方法] | 枚举 | 方法 | 说明 | | ------------------- | ----------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------ | | `HeavenlyStem` | `index() -> usize` / `from_index(usize)` | 天干序号,甲 = 0 … 癸 = 9 | | `EarthlyBranch` | `index() -> usize` / `from_index(usize)` | 地支序号,子 = 0 … 亥 = 11。**不是宫位索引**,换算用 [`earthly_branch_to_palace_index`](/zh/docs/rust/util#earthly_branch_to_palace_index) | | `Palace` | `index() -> usize` / `from_index(usize)` | 宫名在 `PALACES` 里的序号,命宫 = 0、父母 = 1 …… 兄弟 = 11。**不是盘上位置**;`from_index` 对 12 取模、不返回 `Option` | | `FiveElementsClass` | `value() -> usize` | 局数:水二局 2、木三局 3、金四局 4、土五局 5、火六局 6 | | `Gender` | `yin_yang() -> YinYang` | 男为阳、女为阴,决定大限与长生十二神的顺逆 | | `Language` | `as_code() -> &'static str` / `from_code(&str)` | 语言代码 `zh-CN` 等;`from_code` 大小写不敏感,连字符与下划线等价(`zh_cn` 也接受) | | `YinYang` | `as_str() -> &'static str` | 「阳」/「阴」,不参与国际化 | | `FiveElements` | `as_str() -> &'static str` | 「木」「金」「水」「火」「土」,不参与国际化 | ```rust println!("{} {}", HeavenlyStem::Gui.index(), EarthlyBranch::Hai.index()); println!("{:?}", Palace::from_index(4)); println!("{}", FiveElementsClass::Wood3rd.value()); println!("{:?} {}", Gender::Female.yin_yang(), Gender::Female.yin_yang().as_str()); println!("{} {:?}", Language::JaJP.as_code(), Language::from_code("ZH-cn")); ``` **输出** ```text 9 11 Career 3 Yin 阴 ja-JP Some(ZhCN) ``` ### 变体清单 [#变体清单] 只列非「一眼可推」的几个;干支、宫名、星耀的变体名与它们的 key 一一对应。 | 枚举 | 变体 | | ------------------- | ------------------------------------------------------------------------------------------------------------------------------------------ | | `YinYang` | `Yang` `Yin` | | `FiveElements` | `Wood` `Metal` `Water` `Fire` `Earth` | | `FiveElementsClass` | `Water2nd` `Wood3rd` `Metal4th` `Earth5th` `Fire6th` | | `Mutagen` | `Lu` `Quan` `Ke` `Ji` | | `Brightness` | `Miao` `Wang` `De` `Li` `Ping` `Bu` `Xian` | | `StarType` | `Major` `Soft` `Tough` `Adjective` `Flower` `Helper` `Lucun` `Tianma` | | `Scope` | `Origin` `Decadal` `Yearly` `Monthly` `Daily` `Hourly` | | `HoroscopeName` | `Decadal` `Childhood` `Age` `Yearly` `Monthly` `Daily` `Hourly` | | `Gender` | `Male` `Female` | | `Language` | `ZhCN` `ZhTW` `EnUS` `JaJP` `KoKR` `ViVN` | | `LeapMonth` | `NotLeap` `Leap` `LeapFixed`——`by_lunar` 的闰月处理方式;另有 `from_flags(is_leap_month, fix_leap)`、`is_leap_month()`、`fix_leap()` 与 iztro 风格的两个布尔互换 | | `Palace` | 按 `index()` 序:`Soul` `Parents` `Spirit` `Property` `Career` `Friends` `Surface` `Health` `Wealth` `Children` `Spouse` `Siblings` | | `PalaceTarget` | `Index(usize)` `Name(Palace)` `Body` `Original` | `IztroError`、`StarType`、`Scope`、`Algorithm`、`AstroType` 标了 `#[non_exhaustive]`, crate 外的 `match` 必须带兜底分支。将来新增变体因此不是破坏性变更。 `Scope` 比 `HoroscopeName` 少一个 `Childhood`:童限不是独立的查询层级, 它只是大限的显示名——未起运时 `h.decadal.name` 显示「童限」,`Scope::Decadal` 照常用。 `TimeIndex` 是 `u8` 的类型别名,仅作可读性标注,没有额外校验。 *** ## Config [#config] 排盘配置:六个开关加两张可选的自定义表。`Config::default()` 与 JS iztro 的默认配置一致。 | 字段 | 类型 | 默认值 | 说明 | | ------------------ | ----------------------------- | --------- | ----------------- | | `year_divide` | `YearDivide` | `Normal` | 年干支按正月初一还是立春换年 | | `horoscope_divide` | `HoroscopeDivide` | `Normal` | 运限干支与月柱按初一还是节气推 | | `age_divide` | `AgeDivide` | `Normal` | 虚岁按自然农历年还是生日进位 | | `day_divide` | `DayDivide` | `Forward` | 晚子时归次日还是归当天 | | `algorithm` | `Algorithm` | `Default` | 派别:默认或中州派 | | `astro_type` | `AstroType` | `Heaven` | 排盘视角:天盘 / 地盘 / 人盘 | | `overrides` | `Option>` | `None` | 自定义四化与亮度表 | 六个开关的取值语义与流派背景见 [Config 详解](/zh/docs/guide/guides/config)。 `overrides` 标了 `#[serde(skip)]`:它是排盘的**输入**而不是结果, 放进 DTO 会破坏与 JS iztro 的字段契约。因此 JSON 输出里的 `config` 对象只有六个开关, 自定义表不会回显。 ### 构造方法 [#构造方法] 字段都是 `pub`,可以直接改;链式写法更省事: | 方法 | 说明 | | -------------------------------------------------------------- | --------------------- | | `with_astro_type(AstroType) -> Config` | 指定排盘视角 | | `with_mutagens(HeavenlyStem, [StarKey; 4]) -> Config` | 覆盖某个天干的四化表,顺序为禄、权、科、忌 | | `with_brightness(StarKey, [Option; 12]) -> Config` | 覆盖某颗星的十二宫亮度表,索引 0 为寅宫 | ### 查表方法 [#查表方法] | 方法 | 说明 | | ----------------------------------------------------- | ----------------------------- | | `mutagens_of(HeavenlyStem) -> [StarKey; 4]` | 该天干**实际生效**的四化表:有覆盖用覆盖,否则用默认表 | | `brightness_of(StarKey, usize) -> Option` | 该星在该宫实际生效的亮度;宫位索引越界对 12 取模 | **示例** ```rust let cfg = Config::default() .with_astro_type(AstroType::Earth) .with_mutagens(HeavenlyStem::Geng, [ StarKey::TaiyangMaj, StarKey::WuquMaj, StarKey::TianfuMaj, StarKey::TiantongMaj, ]); println!("{:?}", cfg.astro_type); println!("{:?}", cfg.mutagens_of(HeavenlyStem::Geng) .iter().map(|s| translate_star(*s, Language::ZhCN)).collect::>()); println!("{:?}", cfg.mutagens_of(HeavenlyStem::Jia) .iter().map(|s| translate_star(*s, Language::ZhCN)).collect::>()); println!("{:?}", cfg.brightness_of(StarKey::ZiweiMaj, 4)); let chart = by_solar("2000-8-16", 2, Gender::Female, true, Language::ZhCN, cfg)?; println!("{}", translate_five_elements_class(chart.five_elements_class, Language::ZhCN)); ``` **输出** ```text Earth ["太阳", "武曲", "天府", "天同"] ["廉贞", "破军", "武曲", "太阳"] Some(Miao) 土五局 ``` 庚干的四化被换成「太阳禄、武曲权、天府科、天同忌」(默认表是太阴化科), 甲干未被覆盖,仍走默认表。 **边界与陷阱** `with_mutagens` 一次替换某个天干的**全部四位**,不能只换其中一位; `with_brightness` 一次替换某颗星的**全部十二宫**。未提到的天干与星仍用默认表。 `Config` 含 `Option>`,只实现 `Clone`。要在循环里复用同一份配置, 写 `cfg.clone()`——`Arc` 的克隆是引用计数加一,不复制表本身。 四化表影响:生年四化标记、宫干飞星族方法、运限各层级的四化、 `get_mutagen` / `get_mutagens_by_heavenly_stem`。 亮度表影响:星耀的 `brightness` 字段与 `with_brightness` 判断、`get_brightness`。 两者都不改变星耀落宫。 ### TableOverrides [#tableoverrides] `Config` 里那两张表的载体,一般不必直接构造——用上面两个 `with_*` 即可。 需要一次塞多条时可以自己建: | 方法 | 说明 | | ------------------------------------------------------------- | -------------------- | | `set_mutagens(HeavenlyStem, [StarKey; 4])` | 写入某天干的四化表 | | `set_brightness(StarKey, [Option; 12])` | 写入某星的亮度表 | | `mutagens_of(HeavenlyStem) -> Option<&[StarKey; 4]>` | 取被覆盖的四化表,未覆盖为 `None` | | `brightness_of(StarKey) -> Option<&[Option; 12]>` | 取被覆盖的亮度表 | | `is_empty() -> bool` | 是否一条覆盖都没有 | 注意与 `Config` 上同名方法的区别:`TableOverrides::mutagens_of` 只报告**有没有被覆盖**, `Config::mutagens_of` 报告**实际生效**的表(未覆盖时回落到默认表)。 *** ## get\_star\_info [#get_star_info] **用途** 取一颗星的亮度表、五行与阴阳。 **签名** ```rust pub fn get_star_info(key: StarKey) -> Option ``` **参数** | 参数 | 类型 | 必填 | 默认 | 说明 | | ----- | --------- | -- | -- | ---- | | `key` | `StarKey` | 是 | — | 星耀标识 | **返回值** `Option`。只有二十颗星有记录,其余返回 `None`。 `StarInfo` 的字段: | 字段 | 类型 | 说明 | | --------------- | -------------------------- | ----------------------------- | | `brightness` | `[Option; 12]` | 十二宫亮度,索引 0 为寅宫;该宫无亮度则为 `None` | | `five_elements` | `Option` | 五行 | | `yin_yang` | `Option` | 阴阳 | 有记录的二十颗是**十四主星**加文昌、文曲、火星、铃星、擎羊、陀罗, 即 `STARS_WITH_INFO` 常量列出的那些。 **示例** ```rust let info = data::stars::get_star_info(StarKey::ZiweiMaj).unwrap(); println!("五行 {:?} 阴阳 {:?}", info.five_elements, info.yin_yang); println!("寅宫亮度 {:?}", info.brightness[0]); println!("禄存有记录: {}", data::stars::get_star_info(StarKey::LucunMin).is_some()); ``` **输出** ```text 五行 Some(Earth) 阴阳 Some(Yin) 寅宫亮度 Some(Wang) 禄存有记录: false ``` **边界与陷阱** 表中部分星耀的五行或阴阳未填:太阳与七杀两项皆空, 贪狼、天相、天梁、破军的阴阳空,六颗辅星两项皆空。 读到 `None` 表示表里没有这项数据,不是算法产生的中间态。 *** ## get\_heavenly\_stem\_info [#get_heavenly_stem_info] **用途** 取天干的阴阳、五行、对冲天干与四化四星。 **斗数含义** 天干的四化表是四化系统的根:生年干决定生年四化, 宫干决定该宫飞出的四化,运限干决定该层级的四化。 **签名** ```rust pub fn get_heavenly_stem_info(stem: HeavenlyStem) -> HeavenlyStemInfo ``` **返回值** `HeavenlyStemInfo`,字段: | 字段 | 类型 | 说明 | | --------------- | ---------------------- | -------------------- | | `yin_yang` | `YinYang` | 阴阳 | | `five_elements` | `FiveElements` | 五行 | | `crash` | `Option` | 对冲天干;戊、己无对冲,为 `None` | | `mutagen` | `[StarKey; 4]` | 四化四星,顺序为禄、权、科、忌 | **示例** ```rust let jia = data::heavenly_stems::get_heavenly_stem_info(HeavenlyStem::Jia); println!("{:?} {:?} 对冲 {:?}", jia.yin_yang, jia.five_elements, jia.crash); println!("{:?}", jia.mutagen.iter().map(|s| translate_star(*s, Language::ZhCN)).collect::>()); println!("戊干对冲 {:?}", data::heavenly_stems::get_heavenly_stem_info(HeavenlyStem::Wu).crash); ``` **输出** ```text Yang Wood 对冲 Some(Geng) ["廉贞", "破军", "武曲", "太阳"] 戊干对冲 None ``` *** ## get\_earthly\_branch\_info [#get_earthly_branch_info] **用途** 取地支的阴阳、五行、对冲地支、命主身主与身体对应。 **签名** ```rust pub fn get_earthly_branch_info(branch: EarthlyBranch) -> EarthlyBranchInfo ``` **返回值** `EarthlyBranchInfo`,字段: | 字段 | 类型 | 说明 | | --------------- | --------------- | ---------------- | | `yin_yang` | `YinYang` | 阴阳,决定大限与长生十二神的顺逆 | | `five_elements` | `FiveElements` | 五行 | | `crash` | `EarthlyBranch` | 对冲地支 | | `soul` | `StarKey` | 命主星(按命宫地支查) | | `body` | `StarKey` | 身主星(按生年地支查) | | `inside` | `&'static str` | 对应脏腑 | | `outside` | `&'static str` | 对应身体部位 | | `health_tip` | `&'static str` | 健康提示 | `inside` / `outside` / `health_tip` 三项只有中文一种写法,不参与国际化。 **示例** ```rust let zi = data::earthly_branches::get_earthly_branch_info(EarthlyBranch::Zi); println!("{:?} {:?} 对冲 {:?}", zi.yin_yang, zi.five_elements, zi.crash); println!("命主 {} 身主 {}", translate_star(zi.soul, Language::ZhCN), translate_star(zi.body, Language::ZhCN)); println!("{} / {}", zi.inside, zi.outside); ``` **输出** ```text Yang Water 对冲 Wu 命主 贪狼 身主 火星 胆 / 下体 ``` *** ## 顺序常量 [#顺序常量] | 常量 | 类型 | 内容 | | ------------------ | --------------------- | ---------------------------------------------------- | | `HEAVENLY_STEMS` | `[HeavenlyStem; 10]` | 天干顺序:甲乙丙丁戊己庚辛壬癸 | | `EARTHLY_BRANCHES` | `[EarthlyBranch; 12]` | 地支顺序:子丑寅卯辰巳午未申酉戌亥 | | `PALACES` | `[Palace; 12]` | 十二宫名,从命宫起**逆时针**排:命、父母、福德、田宅、官禄、仆役、迁移、疾厄、财帛、子女、夫妻、兄弟 | | `LANGUAGES` | `[&str; 6]` | 支持的语言代码 | | `ZODIAC` | `[&str; 12]` | 生肖标识,按地支顺序 | | `SIGNS` | `[&str; 12]` | 星座标识,按黄道顺序 | | `CHINESE_TIME` | `[&str; 13]` | 时辰标识,早子时起、晚子时止 | | `TIME_RANGES` | `[&str; 13]` | 时辰对应的钟点区间 | | `TIGER_RULE` | `[HeavenlyStem; 10]` | 五虎遁:年干推正月天干 | | `RAT_RULE` | `[HeavenlyStem; 10]` | 五鼠遁:日干推子时天干 | | `MUTAGEN` | `[Mutagen; 4]` | 四化顺序:禄、权、科、忌(在 `data::stars` 下) | `TIGER_RULE` 与 `RAT_RULE` 按天干序号索引:`TIGER_RULE[0]` 是甲年的正月天干。 **示例** ```rust use x_iztro::data::constants::*; println!("{} {} {}", LANGUAGES[0], ZODIAC[0], CHINESE_TIME[12]); println!("{}", TIME_RANGES[2]); println!("甲年正月干 {}", translate_heavenly_stem(TIGER_RULE[0], Language::ZhCN)); println!("甲日子时干 {}", translate_heavenly_stem(RAT_RULE[0], Language::ZhCN)); ``` **输出** ```text en-US rat lateRatHour 03:00~05:00 甲年正月干 丙 甲日子时干 甲 ``` *** ## 星耀枚举清单 [#星耀枚举清单] | 常量 | 长度 | 内容 | | ----------------- | --- | ------------------------- | | `ALL_STARS` | 162 | 全部星耀标识,顺序与 `StarKey` 声明一致 | | `STARS_WITH_INFO` | 20 | 有 `StarInfo` 记录的那二十颗 | **示例** ```rust println!("{} {}", data::stars::ALL_STARS.len(), data::stars::STARS_WITH_INFO.len()); // 列出所有有亮度表的星 for star in data::stars::STARS_WITH_INFO { print!("{} ", translate_star(star, Language::ZhCN)); } ``` **输出** ```text 162 20 紫微 天机 太阳 武曲 天同 廉贞 天府 太阴 贪狼 巨门 天相 天梁 七杀 破军 文昌 文曲 火星 铃星 擎羊 陀罗 ``` *** ## get\_brightness\_table [#get_brightness_table] **用途** 取一颗星的十二宫亮度表原文。 **签名** ```rust pub fn get_brightness_table(key: StarKey) -> Option<[Option; 12]> ``` **返回值** 定长十二项数组,索引 0 为寅宫;该宫无亮度的位置为 `None`。 没有亮度表的星耀返回外层 `None`。 **示例** ```rust let t = data::stars::get_brightness_table(StarKey::ZiweiMaj).unwrap(); println!("{:?}", &t[..4]); println!("{:?}", data::stars::get_brightness_table(StarKey::LucunMin).is_none()); ``` **输出** ```text [Some(Wang), Some(Wang), Some(De), Some(Wang)] true ``` **边界与陷阱** `get_brightness_table` 给的是**内置默认表**,不看配置; [`utils::get_brightness`](/zh/docs/rust/util#get_brightness) 收 `&Config`, 自定义亮度表会改变它的结果。要复核「这张盘上实际用了什么亮度」,用后者。 `get_star_info(key).brightness` 与本函数取值相同,只是顺带给出五行与阴阳。 # 翻译 (/zh/docs/rust/i18n) 标识与译名的双向查找,以及按类目的翻译函数。 星盘上每个字段都同时给出译名与 `*_key` 标识,通常不必手工翻译。 这些函数用于手上只有标识(或只有某种语言的译名)、需要换算的场合。 支持六种语言:`zh-CN`、`zh-TW`、`en-US`、`ja-JP`、`ko-KR`、`vi-VN`。 *** ## translate\_key [#translate_key] **用途** 把任意标识译成指定语言。 **签名** ```rust pub fn translate_key(key: &str, lang: Language) -> Option<&'static str> ``` **参数** | 参数 | 类型 | 必填 | 默认 | 说明 | | ------ | ---------- | -- | -- | ------ | | `key` | `&str` | 是 | — | 语言无关标识 | | `lang` | `Language` | 是 | — | 目标语言 | 覆盖十二类共 260 个标识: | 类目 | 数量 | 例 | | ----------- | --- | --------------------------------------------------------- | | 星耀 | 162 | `ziweiMaj`、`changsheng`、`yunlu` | | 宫位(含身宫、来因宫) | 14 | `soulPalace`、`wealthPalace`、`bodyPalace`、`originalPalace` | | 天干 | 10 | `jiaHeavenly` | | 地支 | 12 | `ziEarthly` | | 亮度 | 7 | `miao`、`wang` | | 四化 | 4 | `sihuaLu` | | 五行局 | 5 | `water2nd` | | 性别 | 2 | `male`、`female` | | 生肖 | 12 | `rat`、`ox` | | 时辰 | 13 | `earlyRatHour` | | 星座 | 12 | `aries` | | 运限层级 | 7 | `decadal`、`turn` | **返回值** `Option<&'static str>`。未知标识返回 `None`。 **示例** ```rust println!("{:?}", translate_key("ziweiMaj", Language::EnUS)); println!("{:?}", translate_key("soulPalace", Language::JaJP)); println!("{:?}", translate_key("nosuch", Language::ZhCN)); ``` **输出** ```text Some("emperor") Some("命宮") None ``` *** ## key\_of [#key_of] **用途** 由任意语言的译名反查标识。 **签名** ```rust pub fn key_of(text: &str) -> Option<&'static str> pub fn key_of_in(text: &str, key_filter: &str) -> Option<&'static str> ``` **参数** | 参数 | 类型 | 必填 | 默认 | 说明 | | ------------ | ------ | -- | -- | ------------------- | | `text` | `&str` | 是 | — | 任一支持语言下的译名 | | `key_filter` | `&str` | 是 | — | 限定标识名须含的子串,用于消歧同形译名 | **返回值** `Option<&'static str>`。查不到返回 `None`。 **示例** ```rust println!("{:?}", key_of("紫微")); println!("{:?}", key_of("emperor")); println!("{:?}", key_of("자미")); println!("{:?}", key_of("查无此名")); ``` **输出** ```text Some("ziweiMaj") Some("ziweiMaj") Some("ziweiMaj") None ``` 三种语言的译名都落到同一个标识。 **边界与陷阱** 少数译名在多个类目下同形:en-US 的 `horse` 既是生肖马也是天马, `dragon` 既是生肖龙也是青龙,ko-KR 的 `사` 既是地支巳也是长生12神的死。 `key_of` 逐语言、每种语言内逐标识取先命中者,顺序与 iztro 的 `kot` 完全一致 (有金标测试逐例守着)。要指定类目就用 `key_of_in`——标识名含该子串才纳入比对: ```rust println!("{:?}", key_of("horse")); // Some("horse")(生肖马) println!("{:?}", key_of_in("horse", "Min")); // Some("tianmaMin")(天马) println!("{:?}", key_of("유시")); // Some("hourly")(流时) println!("{:?}", key_of_in("유시", "Hour")); // Some("roosterHour")(酉时) println!("{:?}", key_of_in("horse", "Palace")); // None ``` 常用子串:`Maj` 十四主星、`Min` 辅星、`Heavenly` / `Earthly` 干支、 `Palace` 宫位、`Hour` 时辰。限定后无匹配返回 `None`,不退回未限定的结果。 `key_of` 会遍历 260 个标识 × 6 种语言。单次调用开销可忽略, 但不要放在每宫每星的内层循环里——那种场合直接用数据自带的 `*_key` 字段。 *** ## all\_keys [#all_keys] **用途** 取全部 260 个可翻译标识。 **签名** ```rust pub fn all_keys() -> Vec<&'static str> ``` **返回值** `Vec<&'static str>`,顺序即 `key_of` 的反查次序: 运限层级、生肖、时辰、星座、五行局、天干、地支、亮度、四化、星耀、宫位、性别, 与 iztro 各语言翻译文件的合并次序一致。 **示例** ```rust use x_iztro::i18n::lookup::{all_keys, translate_key}; let keys = all_keys(); println!("{} 个标识", keys.len()); println!("{:?}", &keys[..4]); println!("{:?}", translate_key(keys[0], Language::ZhCN)); ``` **输出** ```text 260 个标识 ["decadal", "childhood", "yearly", "monthly"] Some("大限") ``` 要遍历某一类目自己的标识时,直接用 `data` 模块的对应常量 (`ALL_STARS`、`PALACES`、`HEAVENLY_STEMS`、`MUTAGEN` 等)更省事。 *** ## 按类目的翻译函数 [#按类目的翻译函数] 标识的类别已知时,用对应的强类型函数更直接,也免去 `Option`。 | 函数 | 入参 | | ------------------------------- | ---------------------- | | `translate_star` | `StarKey` | | `translate_palace` | `Palace` | | `translate_heavenly_stem` | `HeavenlyStem` | | `translate_earthly_branch` | `EarthlyBranch` | | `translate_brightness` | `Brightness` | | `translate_mutagen` | `Mutagen` | | `translate_five_elements_class` | `FiveElementsClass` | | `translate_gender` | `Gender` | | `translate_zodiac` | `EarthlyBranch` | | `translate_time` | `u8`(时辰索引 0–12) | | `translate_sign` | `usize`(星座索引 0–11,白羊起) | | `translate_horoscope_name` | `HoroscopeName` | 全部形如 `fn(值, Language) -> &'static str`,返回静态字符串不分配内存。 **示例** ```rust use x_iztro::i18n::{translate_palace, translate_star}; println!("{}", translate_star(StarKey::ZiweiMaj, Language::ViVN)); println!("{}", translate_palace(Palace::Soul, Language::KoKR)); ``` **输出** ```text Tử Vi 명궁 ``` *** ## 没有全局语言开关 [#没有全局语言开关] x-iztro 不设「当前语言」这样的全局状态:排盘时语言随参数传入, 翻译函数每次调用都显式指定目标语言。 全局语言开关会让同一段代码在不同调用顺序下产出不同结果, 多线程环境尤其危险。显式传参使每次调用的结果只由入参决定。 要在一个进程里同时输出多种语言,直接排多张盘或多次调用翻译函数即可,互不干扰: ```rust let zh = by_solar("2000-8-16", 2, Gender::Female, true, Language::ZhCN, Config::default())?; let en = by_solar("2000-8-16", 2, Gender::Female, true, Language::EnUS, Config::default())?; println!("{} / {}", zh.palace(Palace::Soul).unwrap().major_stars[0].name, en.palace(Palace::Soul).unwrap().major_stars[0].name); ``` **输出** ```text 紫微 / emperor ``` # 扩展星盘 (/zh/docs/rust/extend) 用扩展 trait 给 Astrolabe、PalaceRef 补自定义分析方法。 斗数的分析规则千人千面,库不可能穷举。x-iztro 的做法是让你把自己的规则 以方法的形式挂到星盘上——调用语法与内置方法一致,且在编译期完成,类型受检、零运行期开销。 ## 配方 [#配方] 定义一个 trait,声明你要补的方法 为 `Astrolabe` (或 `PalaceRef` 、 `StarRef` )实现它 使用处 `use` 这个 trait,方法即可用 ```rust use x_iztro::i18n::translate_star; use x_iztro::*; /// 给星盘补两个自定义分析方法。 trait MyAnalysis { /// 命宫主星名(空宫借对宫),多颗以逗号分隔 fn major_star(&self) -> String; /// 五行局的局数 fn five_elements_value(&self) -> usize; } impl MyAnalysis for Astrolabe { fn major_star(&self) -> String { let soul = self.palace(Palace::Soul).expect("命宫必然存在"); let source = if soul.is_empty() { soul.opposite_palace() } else { soul }; source .major_stars .iter() .filter(|s| s.star_type == StarType::Major) .map(|s| translate_star(s.key, self.language)) .collect::>() .join(",") } fn five_elements_value(&self) -> usize { self.five_elements_class.value() } } ``` **用法** ```rust let chart = by_solar("2000-8-16", 2, Gender::Female, true, Language::ZhCN, Config::default())?; println!("{}", chart.major_star()); println!("{}", chart.five_elements_value()); // 扩展方法随排盘语言输出 let en = by_solar("2000-8-16", 2, Gender::Female, true, Language::EnUS, Config::default())?; println!("{}", en.major_star()); ``` **输出** ```text 紫微 3 emperor ``` *** ## 扩展别的类型 [#扩展别的类型] 同一套写法适用于宫位与星耀。为视图类型实现时注意生命周期参数: ```rust trait PalaceAnalysis { /// 本宫是否「煞忌交冲」:坐煞星且带化忌 fn is_afflicted(&self) -> bool; } impl PalaceAnalysis for PalaceRef<'_> { fn is_afflicted(&self) -> bool { use x_iztro::StarKey::*; self.has_one_of(&[QingyangMin, TuoluoMin, HuoxingMin, LingxingMin, DikongMin, DijieMin]) && self.has_mutagen(Mutagen::Ji) } } ``` ```rust for palace in &chart.palaces { let p = chart.palace(palace.index).unwrap(); if p.is_afflicted() { println!("{} 煞忌交冲", translate_palace(p.name, Language::ZhCN)); } } ``` **输出** ```text 疾厄 煞忌交冲 ``` *** ## 组织建议 [#组织建议] `WealthAnalysis`、`CareerAnalysis`、`HealthAnalysis` 各自成 trait, 使用方按需 `use`。堆成一个大 trait 会让所有调用点都被迫引入全部方法。 `s.key == StarKey::ZiweiMaj` 在任何输出语言下都成立; `s.name == "紫微"` 只在中文盘上成立。展示时才翻译。 同一套分析规则要在 Python 与 Go 上都能用时,写成 Rust 扩展没有意义—— 三侧各写一遍才是当前的做法,两侧的扩展方式见各自的「扩展星盘」页。 判断逻辑基于语言无关标识,三份实现断言同一组取值即可保证一致。 *** ## 与运行期注入的区别 [#与运行期注入的区别] 有些库让你在运行期把函数挂到对象上。Rust 的扩展 trait 在编译期完成同一件事, 差别在于: | | 扩展 trait | 运行期注入 | | ------ | ---------- | ------- | | 方法是否存在 | 编译期确定 | 运行期才知道 | | 类型检查 | 有 | 无 | | 调用开销 | 与内置方法相同 | 多一次动态查找 | | 出错时机 | 编译失败 | 运行时报错 | | 作用范围 | `use` 了才可见 | 全局或按实例 | 代价是扩展方法必须在编译期就写好,不能由配置文件或用户输入动态决定。 需要那种灵活度时,用一张 `HashMap bool>` 自行分派。 # 错误处理 (/zh/docs/rust/errors) IztroError 的三个变体、code() 分类标识、BridgeError 与排查方式。 所有收外部输入的入口都返回 `Result`。日期格式、日期存在性、年份范围、 时辰索引这四类都在核心层前置校验,非法输入得到错误值而不是 panic。 ```rust #[non_exhaustive] pub enum IztroError { /// 日期串格式错、日期不存在,或超出公历 1583–9999 InvalidDate(String), /// 时辰索引超出 0–12 InvalidTimeIndex(u8), /// 依赖的历法库没给出本应存在的干支或星座取值 Internal(String), } impl IztroError { /// 机器可读的错误分类标识 pub fn code(&self) -> &'static str } ``` `IztroError` 实现了 `Display` 与 `std::error::Error`,可直接用 `?` 传播、用 `{}` 打印。 ## code() [#code] `Display` 给的是面向人的文案,会随版本调整;要在程序里分支请用 `code()`: | 变体 | `code()` | 含义 | | ------------------ | -------------------- | ------ | | `InvalidDate` | `invalid_date` | 日期非法 | | `InvalidTimeIndex` | `invalid_time_index` | 时辰索引越界 | | `Internal` | `internal` | 库内部缺陷 | 这三个取值与 Python 的 `IztroError.code`、Go 的 `iztro.Error.Code` 是同一套, 跨语言分支逻辑可以照抄。 ```rust let e = by_solar("2000-2-30", 2, Gender::Female, true, Language::ZhCN, Config::default()) .unwrap_err(); println!("{} / {}", e.code(), e); ``` **输出** ```text invalid_date / invalid solar date '2000-2-30': day is out of range for that month ``` `IztroError` 标了 `#[non_exhaustive]`,crate 外无法穷尽匹配——`match` 必须带兜底分支。 将来新增错误分类因此不构成破坏性变更;按 `code()` 分支的代码则完全不受影响。 *** ## InvalidDate [#invaliddate] **触发条件** | 情形 | 例 | 消息 | | --------------- | ------------- | ------------------------------------ | | 格式不是 `YYYY-M-D` | `"2000/8/16"` | `expected 'YYYY-M-D'` | | 年、月、日不是数字 | `"abc-8-16"` | `year is not a number` | | 月份越界 | `"2000-13-1"` | `month must be within 1-12` | | 该月没有这一天 | `"2000-2-30"` | `day is out of range for that month` | | 年份超出支持范围 | `"1500-1-1"` | `year must be within 1583-9999` | 农历专有(只有 `by_lunar` 与 `get_sign_by_lunar_date`、`get_major_star_by_lunar_date` 会碰到): | 情形 | 例 | 消息 | | --------- | ---------------------------------- | ------------------------------------------ | | 该农历年没有这个月 | `by_lunar("2000-13-1", …)` 之外的表内缺月 | `month does not exist in that lunar year` | | 该农历月没有这一天 | `"2000-7-30"`(七月是小月) | `day is out of range for that lunar month` | 农历消息的前缀是 `invalid lunar date '<原串>': `,公历是 `invalid solar date '<原串>': `, 从消息就能看出走的是哪个入口。 **示例** ```rust let cfg = Config::default(); for date in ["2000-13-1", "2000-2-30", "1500-1-1"] { match by_solar(date, 2, Gender::Female, true, Language::ZhCN, cfg.clone()) { Ok(_) => println!("{date}: ok"), Err(e) => println!("{e}"), } } ``` **输出** ```text invalid solar date '2000-13-1': month must be within 1-12 invalid solar date '2000-2-30': day is out of range for that month invalid solar date '1500-1-1': year must be within 1583-9999 ``` 消息里带上了原始输入,便于在批量处理时定位是哪一条数据出的问题。 **边界与陷阱** 1582 年格里历改革当年有一段不存在的日期。底层历法库在这些日期上没有定义, 因此支持范围从改革完成后的 1583 年起算。上限 9999 是农历数据表的覆盖终点。 `"2000-8-16"` 与 `"2000-08-16"` 都接受。分隔符必须是 `-`。 `by_lunar` 会检查该农历年该月是否真的存在,以及该月有多少天(大月 30、小月 29)。 `leap` 标为闰月但那年那月无闰月时不报错,按普通月处理。 *** ## InvalidTimeIndex [#invalidtimeindex] **触发条件** 时辰索引大于 12。 **示例** ```rust let err = by_solar("2000-8-16", 13, Gender::Female, true, Language::ZhCN, Config::default()) .unwrap_err(); println!("{err}"); ``` **输出** ```text time_index must be 0-12, got 13 ``` **边界与陷阱** 子时跨午夜,拆成早子时(索引 0)与晚子时(索引 12),因此合法值有 13 个。 从小时数换算用 [`time_to_index`](/zh/docs/rust/util#time_to_index),它保证结果落在合法范围。 *** ## Internal [#internal] **触发条件** 依赖的历法库 `lunar_rust` 没有交回本应存在的干支或星座取值。 这是库内部缺陷而非调用方过错。 **为什么不 panic** wasm 目标下 panic 即 trap,而且每次 trap 都会永久损耗模块实例的 栈空间。把这类情况落成错误值,非法调用就不会累积损坏 Go 侧的 wasm 实例。 **消息形状** `internal error: <细节>`,`code()` 为 `internal`。 在金标覆盖的全部日期上都没有触发过这个变体。真的碰到请当作 bug 上报, 并附上完整的排盘入参。 *** ## BridgeError [#bridgeerror] 绑定层(C FFI / wasm / PyO3)对外报错的统一形状。Rust 调用方一般用不到它—— 它是 Python 与 Go 侧错误对象的来源。 ```rust pub struct BridgeError { /// invalid_date / invalid_time_index / invalid_argument / internal pub code: &'static str, /// 面向人的错误描述 pub message: String, } impl BridgeError { pub fn invalid_argument(message: impl Into) -> Self pub fn internal(message: impl Into) -> Self } impl From for BridgeError { /* code 与 message 直接沿用 */ } ``` 比 `IztroError` 多一个分类 `invalid_argument`:绑定层收的是字符串而非枚举, 性别、语言、宫名、四化、配置 JSON 这些取值的合法性只能在那一层校验, 落到这个分类下。 三条出口都把它序列化成同一段 JSON: ```json { "error": "invalid solar date '2000-2-30': day is out of range for that month", "code": "invalid_date" } ``` Python 侧变成 `IztroError`(继承 `ValueError`,带 `.code`), Go 侧变成 `*iztro.Error`(带 `Code`、可用 `errors.Is` 比对哨兵)。 ## C FFI [#c-ffi] `x_iztro::ffi` 导出的 C ABI 里,统一查询入口是 `iztro_query`: ```c // include/x_iztro.h char *iztro_query(const char *query_json); void iztro_free_string(char *s); ``` 收一段以 `kind` 选择具体查询的 JSON(如 `{"kind":"getPalaceNames","soulIndex":0}`), 成功返回 `{"value": <结果>}`,失败返回上面那段带 `code` 的错误 JSON。 它把轻量查询、宫位推算、工具函数、安星、数据表、翻译与 Prompt 生成收进一个符号, 不必为每个函数导出一个 C 符号。键名 camelCase,星耀、干支、宫位等标识按 iztro i18n key 传入与返回。 返回的字符串都由调用方用 `iztro_free_string` 归还;这些函数永不返回 `NULL`。 `ffi` 模块标了 `#[doc(hidden)]`,不是给 Rust 调用方用的—— Rust 里直接调 `by_solar` 一族即可。 *** ## 处理方式 [#处理方式] **用 `?` 传播** ```rust fn analyze(date: &str) -> Result { let chart = by_solar(date, 2, Gender::Female, true, Language::ZhCN, Config::default())?; Ok(chart.palace(Palace::Soul).unwrap().major_stars .iter().map(|s| s.name.clone()).collect::>().join(",")) } ``` **分变体处理** ```rust fn describe(date: &str, ti: u8) -> String { match by_solar(date, ti, Gender::Female, true, Language::ZhCN, Config::default()) { Ok(chart) => format!("排盘成功: {}", chart.solar_date), Err(IztroError::InvalidDate(msg)) => format!("日期有误: {msg}"), Err(IztroError::InvalidTimeIndex(t)) => format!("时辰索引 {t} 越界"), Err(e) => format!("其他错误 [{}]: {e}", e.code()), } } println!("{}", describe("2000-8-16", 2)); println!("{}", describe("2000-2-30", 2)); println!("{}", describe("2000-8-16", 13)); ``` **输出** ```text 排盘成功: 2000-8-16 日期有误: invalid solar date '2000-2-30': day is out of range for that month 时辰索引 13 越界 ``` `IztroError` 标了 `#[non_exhaustive]`,crate 外的 `match` 必须带一个 `_` 或 `Err(e)` 兜底分支,否则编译不过。这样将来新增变体不会成为破坏性变更。 **转成自己的错误类型** `IztroError` 实现了 `std::error::Error`,可直接被 `Box`、`anyhow::Error` 或 `thiserror` 的 `#[from]` 接住。 ```rust #[derive(Debug, thiserror::Error)] enum AppError { #[error("排盘失败: {0}")] Chart(#[from] x_iztro::IztroError), } ``` *** ## 关于 panic [#关于-panic] 排盘入口不会因非法**外部输入**而 panic:日期、时辰这些都落成 `IztroError`, 连历法库交不出取值这种内部情形也走 `IztroError::Internal` 而不是 panic。 仍可能 panic 的只有库自身的逻辑缺陷(如断言失败),这类情况应视为 bug 上报。 wasm 上 panic 即 trap,而且每次 trap 都会永久损耗模块实例的栈空间。 因此校验放在核心层而非绑定层——三种语言共用同一道防线。 # 概览 (/zh/docs/python) 包结构、类型体系与阅读本参考的方式。 Python 包是 Rust 核心的类型化封装:计算在 Rust 里完成, Python 侧提供 dataclass 与 StrEnum 构成的强类型 API,零外部依赖。 这一栏是 Python 侧的完整 API 参考——每个函数、类与方法都有独立条目。 ## 安装 [#安装] ```bash pip install x-iztro ``` 要求 Python 3.10 及以上。发行版内含预编译的原生扩展(abi3-py310 轮子), 安装时不需要 Rust 工具链。 `enum.StrEnum` 是 3.11 才进标准库的。`x_iztro.enums` 在 3.10 上自动回退到等价的 `class StrEnum(str, Enum)` 实现——枚举成员照样既是字符串又能补全,两个版本行为一致。 ## 第一张盘 [#第一张盘] ```python from x_iztro import Astro chart = Astro().by_solar("2000-8-16", 2, "female") # 2 = 寅时(03:00–05:00) print(chart.solar_date, chart.lunar_date) # 2000-8-16 二〇〇〇年七月十七 soul = chart.palace("soulPalace") print(" ".join(s.name for s in soul.major_stars)) # 紫微 ``` ## 包结构 [#包结构] | 模块 | 内容 | 本参考对应页 | | ---------------- | ---------------------------------------------------------------------- | -------------------------------------- | | `x_iztro.Astro` | 排盘主类 | [排盘入口](/zh/docs/python/astro) | | `x_iztro.models` | `Astrolabe`、`Palace`、`Star`、`Horoscope`、`ChartConfig` 等 dataclass | [星盘对象](/zh/docs/python/astrolabe) 起的四页 | | `x_iztro.enums` | 全部语言无关标识的 StrEnum,以及 `GenderType`、`LanguageType`、`TimeIndexType` 等类型别名 | [数据表](/zh/docs/python/data)、本页下方 | | `x_iztro.query` | 生肖、星座、命宫主星的轻量查询 | [轻量查询](/zh/docs/python/query) | | `x_iztro.utils` | 索引换算、亮度与四化查表 | [工具函数](/zh/docs/python/util) | | `x_iztro.star` | 按出生数据安星 | [安星模块](/zh/docs/python/star) | | `x_iztro.data` | 星耀与干支数据表、顺序常量 | [数据表](/zh/docs/python/data) | | `x_iztro.i18n` | 标识与译名的双向查找 | [翻译](/zh/docs/python/i18n) | | `x_iztro.plugin` | 给星盘类挂自定义方法 | [扩展星盘](/zh/docs/python/extend) | `models` 是聚合层:`Astrolabe` 实际定义在 `x_iztro.astrolabe`,`Palace` 在 `x_iztro.palace`, `Star` 在 `x_iztro.star_object`,`Horoscope` 在 `x_iztro.horoscope`, `ChartConfig` 在 `x_iztro.config`,`SurroundedPalaces` 在 `x_iztro.surpalaces`。 从 `x_iztro` 顶层或 `x_iztro.models` 导入都拿得到,按哪个都行。 ## 类型别名 [#类型别名] `x_iztro.enums` 里几个 `Literal` 别名,作用是让编辑器在参数写错时立刻标红: | 别名 | 定义 | | ----------------- | --------------------------------------------------------------------------------------- | | `GenderType` | `Literal["male", "female"]` | | `LanguageType` | `Literal["zh-CN", "zh-TW", "en-US", "ja-JP", "ko-KR", "vi-VN"]` | | `TimeIndexType` | `Literal[0, 1, …, 12]` | | `StarTypeLiteral` | `Literal["major", "soft", "tough", "adjective", "flower", "helper", "lucun", "tianma"]` | | `ScopeLiteral` | `Literal["origin", "decadal", "yearly", "monthly", "daily", "hourly"]` | 它们只是类型标注,运行期不做校验——真正的取值校验在核心层,非法值抛 `IztroError`。 ## 枚举即标识 [#枚举即标识] `x_iztro.enums` 里的每个枚举都是 `StrEnum`,其**取值就是语言无关标识**, 可以直接与数据对象上的 `*_key` 字段比较: ```python from x_iztro import MajorStar, PalaceName soul = chart.palace(PalaceName.SOUL) print(soul.major_stars[0].key == MajorStar.ZIWEI) # True ``` 因为是 `StrEnum`,字符串字面量同样有效——`chart.palace("soulPalace")` 与 `chart.palace(PalaceName.SOUL)` 等价。枚举的价值在于 IDE 补全与拼写检查。 `star.name` 随排盘语言变化(中文盘是「紫微」,英文盘是 `emperor`); `star.key` 在任何语言下都是 `ziweiMaj`。所有判断都应基于 `*_key` 字段或内置判断方法。 ## 数据对象是不可变的 [#数据对象是不可变的] 星盘、宫位、星耀都是 `frozen=True` 的 dataclass,构造后不能修改字段。 需要变体时用返回新对象的方法,如 `chart.rearranged(...)`。 ```python try: chart.solar_date = "2001-1-1" except Exception as e: print(type(e).__name__, e) ``` **输出** ```text FrozenInstanceError cannot assign to field 'solar_date' ``` 不可变让星盘可以安全地在多个分析函数之间传递、放进缓存、跨线程共享, 不必担心某一处的修改影响到别处。 ## 条目怎么读 [#条目怎么读] 每个 API 条目按固定八段组织: **用途** —— 一句话说清它做什么 **斗数含义** —— 它在紫微斗数里对应什么概念(纯工程性的函数省略此段) **签名** —— 从源码原样摘出 **参数** —— 名、类型、是否必填、默认值、说明 **返回值** —— 类型与结构 **示例** —— 可直接运行的片段 **输出** —— 该示例的真实运行结果 **边界与陷阱** —— 空值、越界、配置影响、与其他 API 的相互作用 示例统一用同一张盘:**2000 年 8 月 16 日寅时女命**(`by_solar("2000-8-16", 2, "female")`), 方便跨页对照。这张盘的完整数据见[数据结构](/zh/docs/guide/data-model)。 排盘语言不传时默认 `zh-CN`,因此本参考所有输出块里的展示值都是中文。 换语言只改这些展示串,`*_key` 标识与全部判断方法的结果不变。 # 排盘入口 (/zh/docs/python/astro) Astro 类的排盘方法、重排与 AI Prompt 生成。 排盘是一切的起点:给出生日期、时辰、性别,得到一个 `Astrolabe`。 ```python from x_iztro import Astro astro = Astro() ``` `Astro` 无内部状态,实例化一次到处用;也可以每次调用时临时构造。 所有入口在入参非法时抛 `IztroError`(继承自 `ValueError`,因此 `except ValueError` 也接得住)。日期格式与存在性、公历年份范围、时辰索引在核心层前置校验; 性别、语言、宫名这类字符串取值在绑定层校验。 异常带 `.code` 给出机器可读分类,详见[错误处理](/zh/docs/python/errors)。 *** ## ChartConfig [#chartconfig] 排盘配置:六个开关加两张可选的自定义表。冻结的 dataclass,全部字段都有默认值, 默认值与 JS iztro 一致——`ChartConfig()` 与不传 `config` 等价。 | 字段 | 类型 | 默认 | 取值(对应枚举) | | ------------------ | ------------------------------ | ----------- | -------------------------------------------------- | | `year_divide` | `str` | `"normal"` | `normal` 正月初一 / `exact` 立春(`YearDivide`) | | `horoscope_divide` | `str` | `"normal"` | `normal` 初一 / `exact` 节气(`HoroscopeDivide`) | | `age_divide` | `str` | `"normal"` | `normal` 跨年即加 / `birthday` 过生日才加(`AgeDivide`) | | `day_divide` | `str` | `"forward"` | `forward` 晚子时归次日 / `current` 归当天(`DayDivide`) | | `algorithm` | `str` | `"default"` | `default` / `zhongzhou`(`Algorithm`) | | `astro_type` | `str` | `"heaven"` | `heaven` 天盘 / `earth` 地盘 / `human` 人盘(`AstroType`) | | `mutagens` | `dict[str, list[str]] \| None` | `None` | 天干标识 → 四颗星标识(禄、权、科、忌) | | `brightness` | `dict[str, list[str]] \| None` | `None` | 星耀标识 → 十二项亮度标识,索引 0 为寅宫,空串表示该宫无亮度 | 六个开关的取值语义与流派背景见 [Config 详解](/zh/docs/guide/guides/config)。 **方法** | 方法 | 说明 | | ------------------- | -------------------------------------------- | | `to_dict() -> dict` | 转成绑定层接受的 camelCase 配置对象;两张表为 `None` 时不出现在结果里 | **示例** ```python from x_iztro import Astro, ChartConfig, AstroType, HeavenlyStem, MajorStar cfg = ChartConfig( astro_type="earth", mutagens={HeavenlyStem.GENG: [MajorStar.TAIYANG, MajorStar.WUQU, MajorStar.TIANFU, MajorStar.TIANTONG]}, ) print(cfg.to_dict()) chart = Astro().by_solar("2000-8-16", 2, "female", config=cfg) print(chart.five_elements_class, chart.config.astro_type) print(chart.palace("soulPalace").mutagen_star_keys) ``` **输出** ```text {'yearDivide': 'normal', 'horoscopeDivide': 'normal', 'ageDivide': 'normal', 'dayDivide': 'forward', 'algorithm': 'default', 'astroType': 'earth', 'mutagens': {'gengHeavenly': ['taiyangMaj', 'wuquMaj', 'tianfuMaj', 'tiantongMaj']}} 土五局 earth ['tiantongMaj', 'tianjiMaj', 'wenchangMin', 'lianzhenMaj'] ``` 地盘的命宫落在原盘身宫(官禄,丙戌),因此宫干四化按丙干取。 **边界与陷阱** `mutagens` 一次替换某个天干的**全部四位**,`brightness` 一次替换某颗星的**全部十二宫**。 长度是严格校验:四化必须正好四项、亮度必须正好十二项,多一项少一项都抛 `IztroError`。 未列出的天干与星仍用默认表。 两张表的键与值都必须是语言无关标识(`"gengHeavenly"`、`"taiyangMaj"`)。 传 `"庚"`、`"太阳"` 会报 `invalid mutagens key` 一类错误。 枚举成员是 `StrEnum`,直接当键用即可,`to_dict` 会把它们转成字符串。 `to_dict` 只对两张表里的键值做 `str()`,六个开关字段原样带出。 写 `ChartConfig(astro_type=AstroType.EARTH)` 排盘完全正常(`StrEnum` 与字符串等价), 只是 `to_dict()` 的结果里那一项会印成 ``。 要把配置落成干净的 JSON,开关传字符串字面量,或自己 `str()` 一遍。 `chart.config` 由输出 DTO 还原,只含六个开关——两张自定义表是排盘**输入**而非结果, 不进 DTO(这一点与 JS iztro 的字段契约一致)。 但星盘内部保留了你传进来的原件,因此 `chart.rearranged(...)`、`chart.horoscope(...)`、 Prompt 生成这些二次计算仍然用得上那两张表,不会静默丢失。 要把配置记录下来,请在自己的调用侧保存 `ChartConfig` 对象。 *** ## by\_solar [#by_solar] **用途** 由公历日期排出本命盘。 **斗数含义** 紫微斗数以农历为算法基础,但绝大多数人只记得公历生日。 本方法先把公历转农历(含年、月、日、时四柱),再据此安星。 换年的时点受 `year_divide` 影响——正月初一与立春之间出生的人, 两种配置会得到不同的年干支,进而影响四化、命主身主与全部年系星。 **签名** ```python def by_solar( self, solar_date: str, time_index: TimeIndexType, gender: GenderType, *, fix_leap: bool = True, language: LanguageType = "zh-CN", config: ChartConfig | None = None, ) -> Astrolabe ``` **参数** | 参数 | 类型 | 必填 | 默认 | 说明 | | ------------ | --------------------- | -- | --------- | -------------------------------------------------- | | `solar_date` | `str` | 是 | — | 公历日期,格式 `YYYY-M-D`,月日不必补零。支持 1583–9999 年 | | `time_index` | `int` | 是 | — | 时辰索引 0–12。0 为早子时(00:00–01:00),12 为晚子时(23:00–24:00) | | `gender` | `str` | 是 | — | `"male"` 或 `"female"`。决定大限顺逆与长生、博士十二神的排列方向 | | `fix_leap` | `bool` | 否 | `True` | 仅限关键字。是否调整农历闰月。为真时闰月十六日起按下月算(晚子时除外,见下) | | `language` | `str` | 否 | `"zh-CN"` | 输出语言,影响所有译名字段;`*_key` 标识字段不受影响 | | `config` | `ChartConfig \| None` | 否 | `None` | 排盘配置,`None` 取默认 | **返回值** `Astrolabe`——十二宫、四柱、命主身主、五行局俱全的完整星盘。 **示例** ```python from x_iztro import Astro chart = Astro().by_solar("2000-8-16", 2, "female") print(chart.solar_date, "|", chart.lunar_date, "|", chart.chinese_date) print(chart.sign, chart.zodiac, chart.five_elements_class) print("命主", chart.soul, "身主", chart.body) ``` **输出** ```text 2000-8-16 | 二〇〇〇年七月十七 | 庚辰 甲申 丙午 庚寅 狮子座 龙 木三局 命主 破军 身主 文昌 ``` **边界与陷阱** 子时横跨午夜,分早子时(00:00–01:00,属当日)与晚子时(23:00–24:00,属次日)。 两者的日柱不同,紫微起宫也可能差一天,因此必须区分,索引才有 13 个。 不确定时辰索引时用 `utils.time_to_index(hour)` 换算。 进位要同时满足四个条件:该农历月确实是闰月、`fix_leap` 为真、农历日大于 15、 且时辰索引不是 12(晚子时)。四者缺一,月索引就按本月算。 因此只有农历闰月下半月出生的人,`True` 与 `False` 会得到不同的月索引, 进而影响左辅右弼与全部月系星。 星盘上所有判断方法(`has`、`flies_to`、`with_mutagen` 等)都基于语言无关标识, 换语言排盘不会改变任何判断结果,只改变 `name` 一类展示字段。 *** ## by\_lunar [#by_lunar] **用途** 由农历日期排出本命盘。 **斗数含义** 农历日期是斗数的原生输入,跳过公历转换这一步。 知道自己农历生日的人直接用它,结果与用对应公历日期调 `by_solar` 完全一致。 **签名** ```python def by_lunar( self, lunar_date: str, time_index: TimeIndexType, gender: GenderType, *, is_leap_month: bool = False, fix_leap: bool = True, language: LanguageType = "zh-CN", config: ChartConfig | None = None, ) -> Astrolabe ``` **参数** 除以下一项外,其余与 `by_solar` 相同。`gender` 之后的参数只能按关键字传入—— `is_leap_month` 与 `fix_leap` 相邻,位置传参写反了不报错、盘会静默错一个月。 | 参数 | 类型 | 必填 | 默认 | 说明 | | --------------- | ------ | -- | ------- | ------------------------------------- | | `lunar_date` | `str` | 是 | — | 农历日期,格式 `YYYY-M-D`,月份写正数(闰月由下一参数标记) | | `is_leap_month` | `bool` | 否 | `False` | 仅限关键字。该农历月是否为闰月。若那一年那个月本来就没有闰月,此参数不生效 | **返回值** 同 `by_solar`。 **示例** ```python a = Astro().by_lunar("2000-7-17", 2, "female") b = Astro().by_solar("2000-8-16", 2, "female") print(a.solar_date, a.solar_date == b.solar_date) ``` **输出** ```text 2000-8-16 True ``` **边界与陷阱** 传 `True` 但那个月并非闰月时,参数被静默忽略,不报错。 如果需要严格校验,调用前先自行确认该年该月确实有闰月。 *** ## get\_horoscope [#get_horoscope] **用途** 以某张本命盘为起点计算目标日期的运限。 **签名** ```python def get_horoscope( self, astrolabe: Astrolabe, target_date: str | None = None, target_time_index: TimeIndexType | None = None, ) -> Horoscope ``` **参数** | 参数 | 类型 | 必填 | 默认 | 说明 | | ------------------- | ------------- | -- | ------ | ---------------- | | `astrolabe` | `Astrolabe` | 是 | — | 本命盘 | | `target_date` | `str \| None` | 否 | `None` | 目标公历日期;不传取今天 | | `target_time_index` | `int \| None` | 否 | `None` | 目标时辰索引;不传取此刻所属时辰 | **返回值** `Horoscope`,持有传入的星盘。详见[运限对象](/zh/docs/python/horoscope)。 **示例** ```python chart = Astro().by_solar("2000-8-16", 2, "female") h = Astro().get_horoscope(chart, "2025-6-1", 0) print(h.decadal.heavenly_stem + h.decadal.earthly_branch) print(h.yearly.heavenly_stem + h.yearly.earthly_branch) ``` **输出** ```text 庚辰 乙巳 ``` **边界与陷阱** `chart.horoscope("2025-6-1", 0)` 与本方法完全等价,且不必再持有 `Astro` 实例。 `Astro.get_horoscope` 存在是为了让「所有入口都在一个类上」这种用法也成立。 *** ## rearranged [#rearranged] **用途** 以指定干支为命宫重排本盘,返回新盘;原盘不变。 **斗数含义** 中州派把同一组出生数据看作三张盘:天盘以命宫干支起五行局, 地盘以身宫干支起,人盘以福德宫干支起。起局的干支一变,五行局就变, 紫微天府落点、十二宫名、长生十二神、大限小限随之全部重算。 本方法把这个能力放开到**任意干支**。 **签名** ```python def rearranged(self, from_stem: str, from_branch: str) -> Astrolabe ``` 这是 `Astrolabe` 上的方法,不在 `Astro` 类上。 **参数** | 参数 | 类型 | 必填 | 默认 | 说明 | | ------------- | ----- | -- | -- | ----------------------------- | | `from_stem` | `str` | 是 | — | 新命宫的天干标识,`HeavenlyStem` 枚举值域 | | `from_branch` | `str` | 是 | — | 新命宫的地支标识,`EarthlyBranch` 枚举值域 | **返回值** 新的 `Astrolabe`。重算:命宫身宫、五行局、十四主星、十二宫名、 长生十二神、大限小限、命主星,以及随命宫挪位的天伤、天使、天才。 沿用原盘:辅星、其余杂耀、博士十二神、岁前与将前十二神、身主星。 **示例** ```python chart = Astro().by_solar("2000-8-16", 2, "female") # 从原盘身宫的干支起盘,等价于地盘 body = next(p for p in chart.palaces if p.is_body_palace) earth = chart.rearranged(body.heavenly_stem_key, body.earthly_branch_key) print("天盘", chart.five_elements_class, "→ 地盘", earth.five_elements_class) ``` **输出** ```text 天盘 木三局 → 地盘 土五局 ``` **边界与陷阱** 天盘、地盘、人盘用 `ChartConfig(astro_type="earth")` 直接排即可, 两个排盘入口都支持。`rearranged` 是为「从任意干支起盘」准备的。 身主星按**出生年支**查表,与命宫位置无关,重排不改变出生年。 命主星按命宫地支查表,因此会跟着更新。 *** ## astrolabe\_to\_prompt / horoscope\_to\_prompt [#astrolabe_to_prompt--horoscope_to_prompt] **用途** 把星盘或运限渲染成结构化文本,供大模型消费。 **签名** ```python def astrolabe_to_prompt(self, astrolabe: Astrolabe) -> str def horoscope_to_prompt( self, astrolabe: Astrolabe, target_date: str, target_time_index: TimeIndexType, ) -> str ``` **参数** | 参数 | 类型 | 必填 | 默认 | 说明 | | ------------------- | ----------- | -- | -- | ------------ | | `astrolabe` | `Astrolabe` | 是 | — | 星盘对象 | | `target_date` | `str` | 是 | — | 仅运限版本:目标公历日期 | | `target_time_index` | `int` | 是 | — | 仅运限版本:目标时辰索引 | **返回值** `str`——按星盘的排盘语言输出的结构化文本。 **示例** ```python astro = Astro() chart = astro.by_solar("2000-8-16", 2, "female") print(astro.astrolabe_to_prompt(chart)[:46]) ``` **输出** ```text === 基本信息 === 性别: 女 阳历: 2000-8-16 农历: 二〇〇〇年七月十七 ``` **边界与陷阱** 输出语言跟随星盘的 `language`,不单独设置。要英文 prompt 就用英文排盘。 # 星盘对象 (/zh/docs/python/astrolabe) Astrolabe 的字段、定位方法与三方四正判断。 `Astrolabe` 是排盘的产物,也是一切查询的入口。它是 `frozen=True` 的 dataclass, 持有十二宫的全部数据,以及四柱、命主身主、五行局这些盘级信息。 ```python chart = Astro().by_solar("2000-8-16", 2, "female") ``` 本页示例统一用默认的 `zh-CN` 排盘,因此输出里的展示值都是中文。 换语言只改这些展示串,`*_key` 标识与所有判断方法的结果不变。 ## 字段 [#字段] | 字段 | 类型 | 说明 | | ------------------------------- | ----- | ---------- | | `gender` | `str` | 性别译名 | | `solar_date` | `str` | 公历日期,与入参一致 | | `lunar_date` | `str` | 农历日期的中文写法 | | `chinese_date` | `str` | 四柱展示串 | | `time` | `str` | 时辰名 | | `time_range` | `str` | 时辰对应的钟点区间 | | `sign` | `str` | 星座 | | `zodiac` | `str` | 生肖 | | `soul` | `str` | 命主星译名 | | `body` | `str` | 身主星译名 | | `five_elements_class` | `str` | 五行局译名 | | `earthly_branch_of_soul_palace` | `str` | 命宫地支译名 | | `earthly_branch_of_body_palace` | `str` | 身宫地支译名 | 展示字段随 `language` 翻译。要做判断请用下一组的 `*_key` 字段。 | 字段 | 类型 | 说明 | | ----------------------------------- | ----- | --------------------- | | `gender_key` | `str` | `"male"` / `"female"` | | `soul_key` | `str` | 命主星标识 | | `body_key` | `str` | 身主星标识 | | `five_elements_class_key` | `str` | 五行局标识 | | `earthly_branch_of_soul_palace_key` | `str` | 命宫地支标识 | | `earthly_branch_of_body_palace_key` | `str` | 身宫地支标识 | 取值与 `x_iztro.enums` 的枚举一一对应,可直接用 `==` 比较。 | 字段 | 类型 | 说明 | | ----------- | -------------- | ------------------- | | `palaces` | `list[Palace]` | 十二宫,索引 0 为寅宫、11 为丑宫 | | `raw_dates` | `RawDates` | 结构化的农历生日与四柱干支标识 | `palaces` 的索引是**宫位索引**而非宫名顺序:`palaces[0]` 永远是寅宫, 命宫可能落在其中任何一格。取命宫用 `chart.palace("soulPalace")`。 十二宫在**首次访问** `chart.palaces` 时才从底层 DTO 构建并回填反向引用。 只读日期、命主身主这些盘级字段的调用不必为此付出转换开销; 构建后缓存在实例上,之后每次访问都是同一批对象。 `raw_dates` 是 `lunar_date` / `chinese_date` 两个展示串的数据形式, 要做日期运算或按干支查表时用它,不必解析中文串: | 类型 | 字段 | | ---------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------- | | `RawDates` | `lunar_date: RawLunarDate`、`chinese_date: RawChineseDate` | | `RawLunarDate` | `lunar_year: int`、`lunar_month: int`(1–12)、`lunar_day: int`、`is_leap: bool` | | `RawChineseDate` | 四柱的原始干支中文字(不随盘面语言翻译)`yearly` / `monthly` / `daily` / `hourly`(各是 `tuple[str, str]`),以及对应的标识 `yearly_keys` / `monthly_keys` / `daily_keys` / `hourly_keys` | `RawChineseDate` 另有一个方法 `pillar_keys() -> list[tuple[str, str]]`, 按年、月、日、时的顺序一次给出四柱标识,正好是 [`utils.translate_chinese_date`](/zh/docs/python/util#translate_chinese_date) 的入参形状。 ```python rd = chart.raw_dates print(rd.lunar_date.lunar_year, rd.lunar_date.lunar_month, rd.lunar_date.lunar_day, rd.lunar_date.is_leap) print(rd.chinese_date.yearly, rd.chinese_date.yearly_keys) print(rd.chinese_date.pillar_keys()) ``` **输出** ```text 2000 7 17 False ('庚', '辰') ('gengHeavenly', 'chenEarthly') [('gengHeavenly', 'chenEarthly'), ('jiaHeavenly', 'shenEarthly'), ('bingHeavenly', 'wuEarthly'), ('gengHeavenly', 'yinEarthly')] ``` | 字段 | 类型 | 说明 | | ------------ | ------------- | ------------------ | | `time_index` | `int` | 出生时辰索引 | | `fix_leap` | `bool` | 排盘时是否修正闰月 | | `language` | `str` | 输出语言 | | `config` | `ChartConfig` | 排盘配置,由 DTO 还原的六个开关 | 运限、重排与 Prompt 从这四项重新发起计算,因此不必再传一遍排盘参数。 `chart.config` 是从输出 DTO 还原的,只含六个开关; 排盘时传进来的自定义四化 / 亮度表不在里面。 但星盘内部保留了调用方给的原件,因此 `rearranged`、`horoscope`、 Prompt 这些二次计算仍然用得上那两张表——不会静默丢失。 *** ## palace [#palace] **用途** 按索引、宫名、身宫或来因宫取一宫。 **斗数含义** 十二宫是斗数的骨架。命宫定下后,其余十一宫按固定顺序逆时针排开。 「身宫」是十二宫之一同时被标记的那一宫,代表后天着力处; 「来因宫」是宫干与生年干相同的那一宫,代表事情的起因。 **签名** ```python def palace(self, index_or_name: int | PalaceName | str) -> Palace | None ``` **参数** | 参数 | 类型 | 必填 | 默认 | 说明 | | --------------- | ------------ | -- | -- | ------- | | `index_or_name` | `int \| str` | 是 | — | 四种写法见下表 | | 写法 | 例子 | 含义 | | ------ | -------------------------------- | --------------------------- | | 索引 | `chart.palace(0)` | 宫位索引 0–11,0 为寅宫 | | 宫名标识 | `chart.palace("soulPalace")` | 十二宫名标识之一,即 `PalaceName` 的值域 | | 当前语言宫名 | `chart.palace("命宫")` | 与排盘语言一致的宫名文本 | | 身宫 | `chart.palace("bodyPalace")` | 带身宫标记的那一宫 | | 来因宫 | `chart.palace("originalPalace")` | 宫干与生年干相同的那一宫 | **返回值** `Palace | None`。索引越界、宫名拼错时返回 `None`; 宫名、身宫、来因宫三种写法只要拼对,在任何一张盘上都能定位到。 **示例** ```python soul = chart.palace("soulPalace") print(soul.name, soul.heavenly_stem + soul.earthly_branch) print("身宫落在", chart.palace("bodyPalace").name) print("来因宫是", chart.palace("originalPalace").name) print("寅宫是", chart.palace(0).name) ``` **输出** ```text 命宫 壬午 身宫落在 官禄 来因宫是 夫妻 寅宫是 财帛 ``` **边界与陷阱** 来因宫要求宫干与生年干相同,且该宫不在子、丑二宫。 十二宫的天干由五虎遁从寅宫起排,寅到酉这十宫刚好把十天干各走一遍, 子、丑两宫重复了寅、卯的天干——正因为重复才被排除。 于是生年干在寅到酉之间必然命中且只命中一次:任何一张盘上来因宫都存在,且唯一。 身宫同理恒存在。因此 `None` 只可能来自索引越界或名字拼错。 `chart.palace("soulPalce")`(少一个 a)不会报错,只会返回 `None`—— `palace` 用的是逐宫比对而不是查表,比不中就是没有。 下一步再 `.name` 就变成 `AttributeError: 'NoneType' object has no attribute 'name'`, 错误现场离真正的笔误已经隔了一段。 要在写错的当场就发现,用枚举:`PalaceName.SOUL` 有 IDE 补全; 名字来自外部输入时先过一遍构造函数,非法值直接抛 `ValueError`: ```python from x_iztro import PalaceName print(PalaceName("soulPalace")) # StrEnum,打印出来就是它的值 try: PalaceName("soulPalce") except ValueError as e: print("ValueError:", e) ``` **输出** ```text soulPalace ValueError: 'soulPalce' is not a valid PalaceName ``` 同一条规律适用于 `star()`(星名拼错返回 `None`)与 `has()` (星名拼错返回 `False`,因为「集合里没有这个标识」)。 枚举清单见[数据表](/zh/docs/python/data#枚举清单)。 *** ## star / star\_in\_palace [#star--star_in_palace] **用途** 按标识找到一颗星,或同时取回它所在的宫。 **签名** ```python def star(self, star: str) -> Star | None def star_in_palace(self, star: str) -> tuple[Star, Palace] | None ``` **参数** | 参数 | 类型 | 必填 | 默认 | 说明 | | ------ | ----- | -- | -- | ---------------------------------------------- | | `star` | `str` | 是 | — | 星耀标识(如 `"ziweiMaj"`),或**当前排盘语言**下的星名(如 `"紫微"`) | **返回值** 该星不在这张盘上时返回 `None`。 `star_in_palace` 返回 `(星, 宫)` 二元组,省去再调 `star.palace()`。 **示例** ```python ziwei = chart.star("ziweiMaj") print(ziwei.name, "在", ziwei.palace().name) print("对宫是", ziwei.opposite_palace().name) print("亮度", ziwei.brightness, "四化", ziwei.mutagen) star, palace = chart.star_in_palace("ziweiMaj") print(star.key, palace.name_key) ``` **输出** ```text 紫微 在 命宫 对宫是 迁移 亮度 庙 四化 None ziweiMaj soulPalace ``` **边界与陷阱** 只在主星、辅星、杂耀三组里查找。长生十二神、博士十二神、岁前与将前十二神 是每宫一个的标记而非星耀列表,用 `palace.changsheng12_key` 一类字段直接取。 *** ## surrounded\_palaces [#surrounded_palaces] **用途** 取目标宫的三方四正。 **斗数含义** 三方四正是斗数最常用的取象范围:本宫、对宫(本宫 +6)、 官禄位(本宫 +4)、财帛位(本宫 +8)。四个宫合起来看,而不只看本宫, 是因为对宫与三合宫的星耀同样作用于本宫的事。 **签名** ```python def surrounded_palaces(self, index_or_name: int | PalaceName | str) -> SurroundedPalaces | None ``` **参数** 同 `palace`,四种定位写法都支持。 **返回值** `SurroundedPalaces | None`,含 `target` / `opposite` / `wealth` / `career` 四个 `Palace`。 定位不到(索引越界或宫名拼错)时返回 `None`。判断方法见[三方四正](/zh/docs/python/surpalaces)。 **示例** ```python sp = chart.surrounded_palaces("soulPalace") print(sp.target.name, sp.opposite.name, sp.wealth.name, sp.career.name) print("三方四正见紫微:", sp.have(["ziweiMaj"])) ``` **输出** ```text 命宫 迁移 财帛 官禄 三方四正见紫微: True ``` *** ## is\_surrounded / is\_surrounded\_one\_of / not\_surrounded [#is_surrounded--is_surrounded_one_of--not_surrounded] **用途** 直接在星盘上判断某宫的三方四正里有没有指定星耀,省去先取三方四正的一步。 **签名** ```python def is_surrounded(self, index_or_name: int | PalaceName | str, stars: list[str]) -> bool def is_surrounded_one_of(self, index_or_name: int | PalaceName | str, stars: list[str]) -> bool def not_surrounded(self, index_or_name: int | PalaceName | str, stars: list[str]) -> bool ``` **参数** | 参数 | 类型 | 必填 | 默认 | 说明 | | --------------- | ------------ | -- | -- | -------------- | | `index_or_name` | `int \| str` | 是 | — | 定位方式同 `palace` | | `stars` | `list[str]` | 是 | — | 星耀标识列表 | **返回值** | 方法 | 语义 | | ---------------------- | ----------------- | | `is_surrounded` | 列表中**每一颗**都在三方四正里 | | `is_surrounded_one_of` | 列表中**至少一颗**在三方四正里 | | `not_surrounded` | 列表中**一颗都不在**三方四正里 | **示例** ```python print(chart.is_surrounded("soulPalace", ["ziweiMaj", "tianxiangMaj"])) print(chart.is_surrounded_one_of("soulPalace", ["qishaMaj", "pojunMaj"])) print(chart.not_surrounded("soulPalace", ["huoxingMin"])) ``` **输出** ```text True False True ``` 命宫只坐紫微,天相在三方之一的财帛宫,因此第一行为真; 七杀与破军都不在这四宫内,第二行为假。 **边界与陷阱** `stars` 传空列表时,`is_surrounded` 与 `not_surrounded` 返回 `True` (「所有元素都满足」与「没有元素不满足」对空集都成立), `is_surrounded_one_of` 返回 `False`。调用前先确认列表非空。 *** ## horoscope [#horoscope] **用途** 以本盘为起点计算目标日期的运限。 **签名** ```python def horoscope( self, target_date: str | None = None, target_time_index: int | None = None, ) -> Horoscope ``` **参数** | 参数 | 类型 | 必填 | 默认 | 说明 | | ------------------- | ------------- | -- | ------ | ---------------- | | `target_date` | `str \| None` | 否 | `None` | 目标公历日期;不传取今天 | | `target_time_index` | `int \| None` | 否 | `None` | 目标时辰索引;不传取此刻所属时辰 | **返回值** `Horoscope`——持有本盘的运限对象,六个层级的宫位查询不必再传星盘。 详见[运限对象](/zh/docs/python/horoscope)。 **示例** ```python h = chart.horoscope("2025-6-1", 0) print("大限", h.decadal.heavenly_stem + h.decadal.earthly_branch) print("流年", h.yearly.heavenly_stem + h.yearly.earthly_branch) # 两个参数都可省略,取当下 now = chart.horoscope() ``` **输出** ```text 大限 庚辰 流年 乙巳 ``` *** ## to\_dict / to\_json [#to_dict--to_json] **用途** 把星盘导出成与 JS iztro 字段契约一致的 JSON。 **签名** ```python def to_dict(self) -> dict[str, Any] def to_json(self, **kwargs: Any) -> str ``` **参数** | 参数 | 类型 | 必填 | 默认 | 说明 | | -------- | -- | -- | -- | ---------------------------------------------------------- | | `kwargs` | — | 否 | — | 仅 `to_json`:透传给 `json.dumps`,如 `indent=2`、`sort_keys=True` | `to_json` 默认 `ensure_ascii=False`,中文直接落在输出里而不是 `\uXXXX`。 **返回值** `to_dict` 返回底层 DTO 的**深拷贝**——camelCase 键、值按排盘语言翻译, 另带 `*Key` 语言无关标识与排盘上下文。改它不会影响星盘。 `to_json` 返回同一份数据的 JSON 字符串,内容与 iztro 的 `JSON.stringify(astrolabe)` 逐键逐值对应。 **示例** ```python d = chart.to_dict() print(d["solarDate"], d["palaces"][4]["nameKey"]) print(d["config"]["yearDivide"], d["genderKey"], d["timeIndex"]) import json print(json.dumps({k: d[k] for k in ("gender", "solarDate", "lunarDate")}, ensure_ascii=False)) print(len(chart.to_json()) > 10000, chart.to_json()[:1]) ``` **输出** ```text 2000-8-16 soulPalace normal female 2 {"gender": "女", "solarDate": "2000-8-16", "lunarDate": "二〇〇〇年七月十七"} True { ``` 底层 DTO 经原生扩展转成 Python `dict` 时按键名排序, 因此 `to_dict()` / `to_json()` 的顶层键是 `body`、`bodyKey`、`chineseDate`…… 这个次序, 不是 iztro 声明字段的次序。键名与取值逐个对应,只是排列不同; 要固定次序请自己按需要挑键输出。 **边界与陷阱** `Astrolabe`、`Palace`、`Star` 都是 dataclass,但宫位与星耀各自持有一个指回本盘的 引用(`_astrolabe` / `_palace`)。`dataclasses.asdict(chart)` 会顺着这条回指 无限递归,最终 `RecursionError`。 导出一律走 `to_dict()` / `to_json()`——它们直接拿底层 DTO,既不递归也不丢字段。 `config` 只回显六个开关。排盘时传的自定义四化 / 亮度表是**输入**而非结果, 不进 DTO——这一点与 JS iztro 的字段契约一致。要记录用了哪张表, 请在自己的调用侧保存 `ChartConfig`。 `Horoscope` 上有同名的一对方法,形状一致,见[运限对象](/zh/docs/python/horoscope#to_dict--to_json)。 # 宫位对象 (/zh/docs/python/palace) Palace 的字段,以及星耀判断、空宫判断与飞星族的全部方法。 宫位是斗数分析的主战场。`chart.palace(...)` 返回 `Palace`, 它既持有本宫数据,也能回溯所属星盘、对宫与三方四正。 ```python soul = chart.palace("soulPalace") ``` 本页示例统一用默认的 `zh-CN` 排盘,因此输出里的展示值都是中文。 ## 字段 [#字段] | 字段 | 类型 | 说明 | | --------------------------------------- | ------------ | --------------------------- | | `index` | `int` | 宫位索引 0–11,0 为寅宫 | | `name` / `name_key` | `str` | 宫名译名 / 标识 | | `is_body_palace` | `bool` | 是否身宫 | | `is_original_palace` | `bool` | 是否来因宫(宫干与年干相同且不在子丑二宫) | | `heavenly_stem` / `heavenly_stem_key` | `str` | 宫干,决定本宫飞出的四化 | | `earthly_branch` / `earthly_branch_key` | `str` | 宫支,由索引固定:0 为寅、11 为丑 | | `major_stars` | `list[Star]` | 十四主星中落在本宫的,按安放顺序 | | `minor_stars` | `list[Star]` | 十四辅星中落在本宫的 | | `adjective_stars` | `list[Star]` | 杂耀 | | `changsheng12` / `changsheng12_key` | `str` | 长生十二神,每宫恰好一个 | | `boshi12` / `boshi12_key` | `str` | 博士十二神 | | `jiangqian12` / `jiangqian12_key` | `str` | 将前十二神 | | `suiqian12` / `suiqian12_key` | `str` | 岁前十二神 | | `decadal` | `Decadal` | 大限:岁数区间与宫干支 | | `ages` | `list[int]` | 小限经过本宫的虚岁列表 | | `mutagen_star_keys` | `list[str]` | 本宫**宫干**化出的四颗星标识,顺序为禄、权、科、忌 | 主星、辅星、杂耀是**列表**,一宫可以有零到多颗。 长生、博士、将前、岁前十二神是**每宫恰好一个**的标记,十二宫刚好排满一轮, 因此是单值字段而不是列表。 它由**排盘时生效的**四化表算得——自定义四化表(`ChartConfig(mutagens=...)`) 会反映在这里,飞星族方法读的正是它。 生年四化是打在星耀自身 `mutagen_key` 字段上的标记,两者不是一回事。 *** ## has / not\_have / has\_one\_of [#has--not_have--has_one_of] **用途** 判断本宫坐了哪些星。 **斗数含义** 星耀落宫是斗数的基本盘面信息。「命宫坐紫微天相」即 `has(["ziweiMaj", "tianxiangMaj"])`。查找范围覆盖主星、辅星、杂耀三组。 **签名** ```python def has(self, stars: list[str]) -> bool def not_have(self, stars: list[str]) -> bool def has_one_of(self, stars: list[str]) -> bool ``` **参数** | 参数 | 类型 | 必填 | 默认 | 说明 | | ------- | ----------- | -- | -- | ------------------------ | | `stars` | `list[str]` | 是 | — | 星耀标识列表;也接受**当前排盘语言**下的星名 | **返回值** | 方法 | 语义 | | ------------ | ---------- | | `has` | 列表中每一颗都在本宫 | | `not_have` | 列表中一颗都不在本宫 | | `has_one_of` | 列表中至少一颗在本宫 | **示例** ```python from x_iztro import MajorStar, MinorStar soul = chart.palace("soulPalace") print(soul.has([MajorStar.ZIWEI, MajorStar.TIANXIANG])) print(soul.has_one_of([MajorStar.QISHA, MajorStar.ZIWEI])) print(soul.not_have([MinorStar.HUOXING, MinorStar.LINGXING])) ``` **输出** ```text False True True ``` 这张盘的命宫只坐紫微,天相落在财帛宫,因此要求两颗都在的 `has` 为假。 **边界与陷阱** 空列表下 `has` 与 `not_have` 返回 `True`,`has_one_of` 返回 `False`。 比对的是「本宫全部星耀的标识与译名」这个集合,比不中就是没有—— `soul.has(["ziweiMj"])` 返回 `False` 而不报错,看起来跟「命宫没有紫微」一模一样。 外部输入的星名先过一遍枚举构造函数就能当场发现: `MajorStar("ziweiMj")` 抛 `ValueError`。写死在代码里的用枚举成员,靠 IDE 补全。 *** ## has\_mutagen / not\_have\_mutagen [#has_mutagen--not_have_mutagen] **用途** 判断本宫有没有某种四化。 **斗数含义** 本命四化由**生年干**决定,标记打在对应的星上。 一宫「有化禄」意味着这宫里坐着的某颗星被生年干化了禄。 注意这与飞星不同——飞星看的是宫干,本处看的是星上已有的标记。 **签名** ```python def has_mutagen(self, mutagen: Mutagen) -> bool def not_have_mutagen(self, mutagen: Mutagen) -> bool ``` **参数** | 参数 | 类型 | 必填 | 默认 | 说明 | | --------- | ----- | -- | -- | ------------------------------------------------------- | | `mutagen` | `str` | 是 | — | `"sihuaLu"` / `"sihuaQuan"` / `"sihuaKe"` / `"sihuaJi"` | **返回值** `bool`。只扫描 `major_stars` 与 `minor_stars`,**不看杂耀**。 **示例** ```python from x_iztro import Mutagen children = chart.palace("childrenPalace") print("子女宫有化禄:", children.has_mutagen(Mutagen.LU)) print("子女宫无化忌:", children.not_have_mutagen(Mutagen.JI)) ``` **输出** ```text 子女宫有化禄: True 子女宫无化忌: True ``` **边界与陷阱** `has_mutagen` 只看主星与辅星上的四化标记,杂耀即使带标记也不计入(复刻 iztro 的行为)。 生年四化只会落在十四主星与部分辅星上,因此实际盘面上两种口径通常没有差别。 *** ## is\_empty [#is_empty] **用途** 判断本宫是否空宫。 **斗数含义** 「空宫」指没有十四主星坐守的宫。空宫要借对宫主星来看, 是斗数里一个很常见的判断分支。辅星与杂耀默认不影响空宫的成立。 **签名** ```python def is_empty(self, exclude_stars: list[str] | None = None) -> bool ``` **参数** | 参数 | 类型 | 必填 | 默认 | 说明 | | --------------- | ------------------- | -- | ------ | ---------------------------------- | | `exclude_stars` | `list[str] \| None` | 否 | `None` | 追加计入的星耀:本宫无主星、但坐了其中任一颗时,同样**不算**空宫 | **返回值** `bool`。判定顺序是:先看有无主星,有则不空;再看 `exclude_stars`,命中则不空;都不满足才是空宫。 **示例** ```python parents = chart.palace("parentsPalace") print("父母宫空宫:", parents.is_empty()) print("仆役宫空宫:", chart.palace("friendsPalace").is_empty()) # 父母宫无主星,但坐了陀罗——把陀罗也计入后就不算空宫 print("父母宫计入陀罗:", parents.is_empty(["tuoluoMin"])) ``` **输出** ```text 父母宫空宫: True 仆役宫空宫: False 父母宫计入陀罗: False ``` 这张盘只有父母、田宅两宫无主星。仆役宫坐太阴,因此不算空宫。 **边界与陷阱** `exclude_stars` 不是「判断时忽略这些星」,而是「这些星也算数」。 本宫已有主星时它完全不起作用——有主星就直接不是空宫,不再看这个列表。 不传 `exclude_stars` 时只检查 `major_stars`。一宫辅星杂耀满座但没有主星,仍然是空宫。 *** ## flies\_to / flies\_one\_of\_to / not\_fly\_to [#flies_to--flies_one_of_to--not_fly_to] **用途** 判断本宫宫干的四化是否飞入目标宫。 **斗数含义** 飞星派的核心手法。每个宫位有自己的宫干,宫干按四化表决定 哪四颗星化禄、权、科、忌。若被化的那颗星恰好坐在目标宫,就叫「本宫化 X 入目标宫」。 「命宫化禄入财帛」表达的是命宫这件事的顺遂落在财帛上。 **签名** ```python def flies_to(self, target: Palace | int | str, mutagens: Mutagen | list[Mutagen]) -> bool def flies_one_of_to(self, target: Palace | int | str, mutagens: Mutagen | list[Mutagen]) -> bool def not_fly_to(self, target: Palace | int | str, mutagens: Mutagen | list[Mutagen]) -> bool ``` **参数** | 参数 | 类型 | 必填 | 默认 | 说明 | | ---------- | ---------------------- | -- | -- | -------------------------------------------------- | | `target` | `Palace \| int \| str` | 是 | — | 目标宫:宫位对象、索引、宫名、`"bodyPalace"` 或 `"originalPalace"` | | `mutagens` | `str \| list[str]` | 是 | — | 要检查的四化,单个或列表 | **返回值** | 方法 | 语义 | | ----------------- | ------------------ | | `flies_to` | 列出的四化**全部**飞入目标宫 | | `flies_one_of_to` | 列出的四化**至少一个**飞入目标宫 | | `not_fly_to` | 列出的四化**一个都不**飞入目标宫 | **示例** ```python from x_iztro import Mutagen, PalaceName soul = chart.palace("soulPalace") print("命宫化禄入财帛:", soul.flies_to(PalaceName.WEALTH, Mutagen.LU)) print("命宫化禄或忌入迁移:", soul.flies_one_of_to(PalaceName.SURFACE, [Mutagen.LU, Mutagen.JI])) print("命宫不化权入子女:", soul.not_fly_to(PalaceName.CHILDREN, Mutagen.QUAN)) ``` **输出** ```text 命宫化禄入财帛: False 命宫化禄或忌入迁移: False 命宫不化权入子女: True ``` **边界与陷阱** `mutagens` 传空列表时 `flies_to` 返回 `False`, `flies_one_of_to` 与 `not_fly_to` 返回 `True`。 这与「空集上全称命题为真」的直觉相反,但复刻的是 iztro 的行为: `flies_to` 先算出要找的星,一颗都没有就直接判假。传空通常是调用方的疏漏。 `target` 写成越界索引或拼错的宫名时,三个方法一律返回 `False`, 包括语义上「否定」的 `not_fly_to`——定位失败不等于「没飞进去」。 `ChartConfig(mutagens=...)` 换掉某个天干的四化表后,宫干落在该天干的宫飞出的星随之改变。 飞星族方法读的是排盘时生效的表,不是内置默认表。 目标宫写成本宫时,语义上是「自化」。此时用 `self_mutaged` 一族更直观。 *** ## self\_mutaged / self\_mutaged\_one\_of / not\_self\_mutaged [#self_mutaged--self_mutaged_one_of--not_self_mutaged] **用途** 判断本宫是否自化。 **斗数含义** 自化指本宫宫干化出的星恰好就坐在本宫。 含义上是「自己把自己的能量释放掉」,与飞入他宫的定向作用不同。 **签名** ```python def self_mutaged(self, mutagens: Mutagen | list[Mutagen]) -> bool def self_mutaged_one_of(self, mutagens: Mutagen | list[Mutagen] | None = None) -> bool def not_self_mutaged(self, mutagens: Mutagen | list[Mutagen] | None = None) -> bool ``` **参数** | 参数 | 类型 | 必填 | 默认 | 说明 | | ---------- | -------------------------- | --- | ------ | ----------------------- | | `mutagens` | `str \| list[str] \| None` | 视方法 | `None` | 要检查的四化;后两个方法省略时表示「四化全部」 | **返回值** | 方法 | 语义 | | --------------------- | ----------------------- | | `self_mutaged` | 列出的四化全部自化 | | `self_mutaged_one_of` | 列出的四化至少一个自化;省略参数时检查全部四化 | | `not_self_mutaged` | 列出的四化一个都不自化;省略参数时检查全部四化 | **示例** ```python career = chart.palace("careerPalace") print("官禄宫自化禄:", career.self_mutaged(Mutagen.LU)) print("官禄宫自化忌:", career.self_mutaged(Mutagen.JI)) print("官禄宫有任一自化:", career.self_mutaged_one_of()) print("官禄宫无任何自化:", career.not_self_mutaged()) ``` **输出** ```text 官禄宫自化禄: False 官禄宫自化忌: True 官禄宫有任一自化: True 官禄宫无任何自化: False ``` 官禄宫宫干为丙,丙干化忌在廉贞,而廉贞正坐官禄宫,故成自化忌。 **边界与陷阱** `self_mutaged_one_of` 与 `not_self_mutaged` 把不传参数(或空列表)解释为「全部四化」。 `self_mutaged` 不做这层回退,空列表退化成「本宫是否包含空集」,恒为 `True`—— 与 `flies_to` 的空列表判假正好相反,别把两者的直觉混用。 *** ## mutaged\_places / mutagen\_stars [#mutaged_places--mutagen_stars] **用途** 取本宫宫干化出的四颗星分别落在哪些宫,或直接取那四颗星本身。 **斗数含义** 飞星分析的全景版本:不问「有没有飞到某宫」,而是一次拿到禄权科忌的落点。 **签名** ```python def mutaged_places(self, all_palaces: list[Palace] | None = None) -> list[Palace | None] def mutagen_stars(self, mutagens: Mutagen | list[Mutagen]) -> list[str] ``` **参数** | 参数 | 类型 | 必填 | 默认 | 说明 | | ------------- | ---------------------- | -- | ------ | ------------ | | `all_palaces` | `list[Palace] \| None` | 否 | `None` | 通常不传,宫位已持有星盘 | | `mutagens` | `str \| list[str]` | 是 | — | 要取的四化位 | **返回值** `mutaged_places` 返回长度为 4 的列表,顺序为**禄、权、科、忌**, 某颗被化的星不在盘上时对应位置为 `None`。 `mutagen_stars` 返回星耀标识列表,顺序与传入的四化一致。 **示例** ```python soul = chart.palace("soulPalace") for m, place in zip(["禄", "权", "科", "忌"], soul.mutaged_places()): print(f"化{m} →", place.name if place else "不在盘上") print(soul.mutagen_stars([Mutagen.LU, Mutagen.JI])) ``` **输出** ```text 化禄 → 子女 化权 → 命宫 化科 → 官禄 化忌 → 财帛 ['tianliangMaj', 'wuquMaj'] ``` 命宫宫干为壬,壬干四化为天梁化禄、紫微化权、左辅化科、武曲化忌, 四颗星分别坐在子女、命宫、官禄、财帛四宫。 不传 `all_palaces` 时检索范围是本宫所属星盘的十二宫。 脱离星盘单独构造的宫位既没有星盘也没传范围,此时返回**空列表**而不是四个 `None`。 `mutaged_places` 恒按禄、权、科、忌四位取,不受参数影响; 要挑其中几位用 `mutagen_stars`。 *** ## opposite\_palace / surrounded\_palaces / astrolabe [#opposite_palace--surrounded_palaces--astrolabe] **用途** 从宫位回溯到对宫、三方四正与所属星盘。 **签名** ```python def opposite_palace(self) -> Palace | None def surrounded_palaces(self) -> SurroundedPalaces | None def astrolabe(self) -> Astrolabe | None ``` **返回值** 脱离星盘单独构造的宫位返回 `None`;由星盘查询得到的宫位必然非空。 **示例** ```python soul = chart.palace("soulPalace") print(soul.name, "的对宫是", soul.opposite_palace().name) print("三方四正见煞:", soul.surrounded_palaces().have_one_of(["huoxingMin", "lingxingMin"])) print(soul.astrolabe().five_elements_class) ``` **输出** ```text 命宫 的对宫是 迁移 三方四正见煞: True 木三局 ``` # 星耀对象 (/zh/docs/python/star-object) Star 的字段与亮度、四化判断,以及回溯所在宫的能力。 `Star` 是一颗落在某宫的星,带着它的类型、亮度与四化标记,并能回溯所在宫。 ```python ziwei = chart.star("ziweiMaj") ``` 本页示例统一用默认的 `zh-CN` 排盘,因此输出里的展示值都是中文。 ## 字段 [#字段] | 字段 | 类型 | 说明 | | ---------------- | ------------- | ------------------------------ | | `key` | `str` | 星耀标识,与语言无关,判断时用它 | | `name` | `str` | 星名,按排盘语言翻译 | | `type` | `str` | 星耀类型,见下表 | | `scope` | `str` | 作用范围:本命星为 `"origin"`,流耀为对应运限层级 | | `brightness` | `str \| None` | 亮度译名;没有亮度表的星耀为 `None` | | `brightness_key` | `str \| None` | 亮度标识 | | `mutagen` | `str \| None` | 生年四化译名;未被生年干化的星为 `None` | | `mutagen_key` | `str \| None` | 四化标识 | ### 星耀类型的八个取值 [#星耀类型的八个取值] | 取值 | 含义 | 典型成员 | | ----------- | ---- | ----------------- | | `major` | 十四主星 | 紫微、天府、七杀、破军 | | `soft` | 吉星 | 左辅、右弼、文昌、文曲、天魁、天钺 | | `tough` | 煞星 | 擎羊、陀罗、火星、铃星、地空、地劫 | | `adjective` | 杂耀 | 三台、八座、天刑、天姚 | | `flower` | 桃花星 | 红鸾、天喜、咸池 | | `helper` | 解神 | 解神 | | `lucun` | 禄存 | 禄存 | | `tianma` | 天马 | 天马 | 禄存与天马各自独占一类,因为它们在传统分法里既非纯吉也非纯煞,判断时常单独拎出来。 只有十四主星与文昌、文曲、火星、铃星、擎羊、陀罗这二十颗有亮度表, 其余星耀本就没有亮度概念,`brightness` 为 `None`。 *** ## with\_brightness [#with_brightness] **用途** 判断这颗星是否处于给定亮度之一。 **斗数含义** 亮度(庙旺得利平不陷)描述星耀在该宫位的强弱。 同一颗星在十二宫各有定值,庙旺则力量充分发挥,落陷则受制。 **签名** ```python def with_brightness(self, brightness: Brightness | list[Brightness]) -> bool ``` **参数** | 参数 | 类型 | 必填 | 默认 | 说明 | | ------------ | ------------------ | -- | -- | --------------------- | | `brightness` | `str \| list[str]` | 是 | — | 亮度标识,单个或列表;列表时命中任一即为真 | **返回值** `bool`。该星无亮度时恒为假。 **示例** ```python from x_iztro import Brightness ziwei = chart.star("ziweiMaj") print(ziwei.with_brightness(Brightness.MIAO)) print(ziwei.with_brightness([Brightness.WANG, Brightness.DE])) ``` **输出** ```text True False ``` **边界与陷阱** 列表的语义是「命中任一」而非「全部命中」——一颗星只有一个亮度, 要求全部命中在列表多于一项时永假。 *** ## with\_mutagen [#with_mutagen] **用途** 判断这颗星是否带指定的生年四化。 **斗数含义** 生年四化由出生年干决定,一年固定四颗星分别化禄、权、科、忌。 这个标记跟着星走,无论那颗星落在哪一宫。 **签名** ```python def with_mutagen(self, mutagen: Mutagen | list[Mutagen]) -> bool ``` **参数** | 参数 | 类型 | 必填 | 默认 | 说明 | | --------- | ------------------ | -- | -- | --------------------- | | `mutagen` | `str \| list[str]` | 是 | — | 四化标识,单个或列表;列表时命中任一即为真 | **返回值** `bool`。该星未被生年干化时恒为假。 **示例** ```python from x_iztro import Mutagen print("紫微化禄:", chart.star("ziweiMaj").with_mutagen(Mutagen.LU)) print("太阳化禄:", chart.star("taiyangMaj").with_mutagen(Mutagen.LU)) ``` **输出** ```text 紫微化禄: False 太阳化禄: True ``` 这张盘生年干为庚,庚干太阳化禄,因此标记落在太阳而非紫微。 **边界与陷阱** `with_mutagen` 看的是**生年干**给这颗星打的标记,一张盘上只有四颗星带标记。 宫干飞出的四化不在这里体现,用宫位的 [`flies_to`](/zh/docs/python/palace#flies_to--flies_one_of_to--not_fly_to) 一族。 *** ## palace / opposite\_palace / surrounded\_palaces [#palace--opposite_palace--surrounded_palaces] **用途** 从星回溯到它所在的宫、该宫的对宫与三方四正。 **签名** ```python def palace(self) -> Palace | None def opposite_palace(self) -> Palace | None def surrounded_palaces(self) -> SurroundedPalaces | None ``` **返回值** 脱离星盘单独构造的星耀返回 `None`;由星盘查询得到的星耀必然非空。 **示例** ```python ziwei = chart.star("ziweiMaj") print(ziwei.palace().name) print(ziwei.opposite_palace().name) print("同宫或三方见天相:", ziwei.surrounded_palaces().have(["tianxiangMaj"])) ``` **输出** ```text 命宫 迁移 同宫或三方见天相: True ``` **边界与陷阱** 一颗星在一张盘上只出现一次,因此 `chart.star(key)` 的结果唯一。 运限流耀不在本命盘的星耀列表里,要取它们用运限对象的 [`palace`](/zh/docs/python/horoscope#palace) 配合层级参数。 # 三方四正 (/zh/docs/python/surpalaces) SurroundedPalaces 的四个宫位与五个判断方法。 三方四正是斗数最常用的取象范围。看一件事不能只看本宫, 对宫与两个三合宫的星耀同样作用其上,四宫合看才完整。 本页示例统一用默认的 `zh-CN` 排盘,因此输出里的展示值都是中文。 ## 四个宫位 [#四个宫位] | 字段 | 相对本宫 | 传统称呼 | 意义 | | ---------- | ---- | ---- | -------------- | | `target` | +0 | 本宫 | 事情本身 | | `opposite` | +6 | 对宫 | 与本宫相对的一面,影响最直接 | | `career` | +4 | 官禄位 | 三合之一 | | `wealth` | +8 | 财帛位 | 三合之一 | 四个字段都是 `Palace`,[宫位对象](/zh/docs/python/palace)的全部方法都能用。 `wealth` 与 `career` 指的是「相对本宫的三合位置」,不是十二宫里那两个固定的宫名。 以命宫起算时它们恰好落在财帛宫与官禄宫(+8 与 +4),名字就是这么对上的; 以别的宫起算则是别的宫。 ## 三种取法 [#三种取法] ```python # 从星盘取 sp = chart.surrounded_palaces("soulPalace") # 从宫位取 sp = chart.palace("soulPalace").surrounded_palaces() # 从星耀取(该星所在宫的三方四正) sp = chart.star("ziweiMaj").surrounded_palaces() ``` 三者结果相同,选哪个取决于手上已有什么。 *** ## have / not\_have / have\_one\_of [#have--not_have--have_one_of] **用途** 判断四宫合起来有没有指定星耀。 **斗数含义** 「三方四正见紫微」这类说法,问的正是这四宫里出没出现某颗星, 而不问具体落在其中哪一宫。 **签名** ```python def have(self, stars: list[str]) -> bool def not_have(self, stars: list[str]) -> bool def have_one_of(self, stars: list[str]) -> bool ``` **参数** | 参数 | 类型 | 必填 | 默认 | 说明 | | ------- | ----------- | -- | -- | ------------------------ | | `stars` | `list[str]` | 是 | — | 星耀标识列表;也接受**当前排盘语言**下的星名 | **返回值** | 方法 | 语义 | | ------------- | -------------------- | | `have` | 列表中每一颗都出现在这四宫(不要求同宫) | | `not_have` | 列表中一颗都没出现 | | `have_one_of` | 列表中至少一颗出现 | **示例** ```python from x_iztro import MajorStar, MinorStar sp = chart.surrounded_palaces("soulPalace") print(sp.have([MajorStar.ZIWEI, MajorStar.TIANXIANG])) print(sp.have_one_of([MajorStar.QISHA, MajorStar.POJUN])) print(sp.not_have([MinorStar.HUOXING])) ``` **输出** ```text True False True ``` 紫微在命宫、天相在财帛宫,分处两宫但都在这四宫内,因此 `have` 为真。 **边界与陷阱** `have([A, B])` 的语义是「A 和 B 都出现在这四宫里」, 不要求它们坐在同一宫。要判断同宫,用宫位的 [`has`](/zh/docs/python/palace#has--not_have--has_one_of)。 `have` 与 `not_have` 在空列表下返回 `True`,`have_one_of` 返回 `False`。 *** ## have\_mutagen / not\_have\_mutagen [#have_mutagen--not_have_mutagen] **用途** 判断四宫里有没有某种生年四化。 **斗数含义** 「三方四正见忌」意味着这组宫位里坐着一颗被生年干化忌的星, 是判断压力来源的常用条件。 **签名** ```python def have_mutagen(self, mutagen: Mutagen) -> bool def not_have_mutagen(self, mutagen: Mutagen) -> bool ``` **参数** | 参数 | 类型 | 必填 | 默认 | 说明 | | --------- | ----- | -- | -- | ------ | | `mutagen` | `str` | 是 | — | 四化标识之一 | **返回值** `bool`。 **示例** ```python from x_iztro import Mutagen sp = chart.surrounded_palaces("soulPalace") print("三方四正见禄:", sp.have_mutagen(Mutagen.LU)) print("三方四正见忌:", sp.have_mutagen(Mutagen.JI)) print("三方四正不见科:", sp.not_have_mutagen(Mutagen.KE)) ``` **输出** ```text 三方四正见禄: False 三方四正见忌: False 三方四正不见科: True ``` 这张盘的生年四化落在四宫:太阳化禄在子女、武曲化权在财帛、太阴化科在仆役、天同化忌在疾厄。 命宫的三方四正是命宫、迁移、财帛、官禄——只有化权那一颗落在里面, 因此查禄、查忌都是 `False`,查权则会是 `True`。 **边界与陷阱** 这里看的是**生年四化**打在星上的标记,与宫干飞出的四化无关。 后者请用宫位的飞星族方法。 # 运限对象 (/zh/docs/python/horoscope) 六个运限层级的数据结构,以及不必再传星盘的宫位查询方法。 运限把本命盘投影到某个时间点上。同一张盘,不同年份看到的宫位分布不同—— 这正是「大限走到哪一宫」的意思。 ```python h = chart.horoscope("2025-6-1", 0) ``` `Horoscope` 持有发起它的那张本命盘,因此所有查询方法都不必再把星盘传进去。 每个查询方法末尾那个 `astrolabe=None` 参数是为「手里只有运限数据、星盘另存」的场合留的, 日常用不着传。 本页示例统一用默认的 `zh-CN` 本命盘,因此输出里的展示值都是中文。 ## 字段 [#字段] | 字段 | 类型 | 说明 | | --------------------------------------------------- | ----- | ---------------- | | `solar_date` | `str` | **目标**公历日期,与入参一致 | | `lunar_date` | `str` | 目标日期的农历中文写法 | | `decadal` `age` `yearly` `monthly` `daily` `hourly` | 见下 | 六个运限层级 | `solar_date` 是目标日期不是出生日期;出生日期在本命盘上,用 `h.astrolabe().solar_date` 取。 ## 六个层级 [#六个层级] | 字段 | 类型 | 跨度 | 说明 | | --------- | ----------------- | --- | ------------- | | `decadal` | `HoroscopeItem` | 十年 | 大限。未起运的幼年期为童限 | | `age` | `AgeItem` | 一年 | 小限。按虚岁逐年走一宫 | | `yearly` | `HoroscopeYearly` | 一年 | 流年。按流年干支定宫 | | `monthly` | `HoroscopeItem` | 一月 | 流月 | | `daily` | `HoroscopeItem` | 一日 | 流日 | | `hourly` | `HoroscopeItem` | 一时辰 | 流时 | 两者都是一年一走,但起法不同:小限从生年地支起、按虚岁顺推, 流年直接看那一年的干支落在哪一宫。两条线互相独立,斗数里通常并看。 ### HoroscopeItem [#horoscopeitem] | 字段 | 类型 | 说明 | | --------------------------------------- | -------------------------- | ------------------------- | | `index` | `int` | 该层级落在哪一宫(宫位索引) | | `name` | `str` | 层级显示名,按输出语言翻译 | | `heavenly_stem` / `heavenly_stem_key` | `str` | 该层级的天干,决定它飞出的四化 | | `earthly_branch` / `earthly_branch_key` | `str` | 该层级的地支 | | `palace_names` / `palace_name_keys` | `list[str]` | 以该层级所在宫为命宫重推的十二宫名,按宫位索引排列 | | `mutagen` / `mutagen_keys` | `list[str]` | 该层级天干引发的四化星,顺序为禄权科忌 | | `stars` | `list[list[Star]] \| None` | 该层级的流耀分布;无流耀的层级为 `None` | `AgeItem` 与 `HoroscopeYearly` 继承 `HoroscopeItem`,各自多一个字段: | 类型 | 多出的字段 | 说明 | | ----------------- | -------------------------------- | ----------- | | `AgeItem` | `nominal_age: int` | 该日期对应的虚岁 | | `HoroscopeYearly` | `yearly_dec_star: YearlyDecStar` | 流年的岁前与将前十二神 | ```python class YearlyDecStar: jiangqian12: list[str] # 流年将前十二神译名,按宫位索引排列 jiangqian12_keys: list[str] # 对应标识 suiqian12: list[str] # 流年岁前十二神译名 suiqian12_keys: list[str] # 对应标识 ``` 因为是继承而不是包装,通用字段直接访问就行:写 `h.yearly.heavenly_stem`, 没有 Rust 侧那层 `.base`。 ```python h = chart.horoscope("2025-6-1", 0) print(h.yearly.heavenly_stem, h.yearly.earthly_branch, h.age.nominal_age) print(h.yearly.yearly_dec_star.suiqian12[:3]) print(h.yearly.yearly_dec_star.jiangqian12_keys[:3]) ``` **输出** ```text 乙 巳 26 ['天德', '吊客', '病符'] ['jiesha', 'zhaisha', 'tiansha'] ``` **示例** ```python h = chart.horoscope("2025-6-1", 0) for item in (h.decadal, h.monthly, h.daily, h.hourly): print(f"{item.name} 落在宫位 {item.index} 干支 {item.heavenly_stem}{item.earthly_branch}") print("小限虚岁", h.age.nominal_age) print("大限四化", h.decadal.mutagen) ``` **输出** ```text 大限 落在宫位 2 干支 庚辰 流月 落在宫位 3 干支 壬午 流日 落在宫位 8 干支 辛丑 流时 落在宫位 8 干支 戊子 小限虚岁 26 大限四化 ['太阳', '武曲', '太阴', '天同'] ``` *** ## age\_palace [#age_palace] **用途** 取小限当年所在的宫。 **斗数含义** 小限是逐年推移的一条线,落在哪一宫就以那宫为该年重点。 **签名** ```python def age_palace(self, astrolabe: Astrolabe | None = None) -> Palace | None ``` **参数** | 参数 | 类型 | 必填 | 默认 | 说明 | | ----------- | ------------------- | -- | ------ | ------------- | | `astrolabe` | `Astrolabe \| None` | 否 | `None` | 通常不传,运限已持有本命盘 | **返回值** `Palace | None`——本命盘上的宫位。 运限已持有本命盘,因此实际不会是 `None`;只有手工构造、既没绑星盘也没传 `astrolabe` 的运限对象才拿不到。 **示例** ```python h = chart.horoscope("2025-6-1", 0) print(h.age_palace().name) ``` **输出** ```text 田宅 ``` *** ## palace [#palace] **用途** 取某个运限层级下、按该层级重推的十二宫中的某一宫。 **斗数含义** 大限走到某宫后,以那一宫为「大限命宫」重排十二宫。 「大限的夫妻宫」问的就是这套重排后的宫位,与本命夫妻宫通常不是同一宫。 **签名** ```python def palace( self, name: PalaceName | str, scope: Scope | ScopeLiteral, astrolabe: Astrolabe | None = None, ) -> Palace | None ``` **参数** | 参数 | 类型 | 必填 | 默认 | 说明 | | ----------- | ------------------- | -- | ------ | ----------- | | `name` | `str` | 是 | — | 要取的宫名标识 | | `scope` | `str` | 是 | — | 在哪个层级的十二宫里找 | | `astrolabe` | `Astrolabe \| None` | 否 | `None` | 通常不传 | **返回值** `Palace | None`——本命盘上的宫位(同一格宫位在不同层级有不同宫名)。 层级为 `"origin"` 时即本命十二宫。宫名或层级标识拼错时返回 `None`,不报错。 **示例** ```python from x_iztro import PalaceName, Scope h = chart.horoscope("2025-6-1", 0) print("大限命宫落在本命的", h.palace(PalaceName.SOUL, Scope.DECADAL).name) print("本命命宫是", h.palace(PalaceName.SOUL, Scope.ORIGIN).name) ``` **输出** ```text 大限命宫落在本命的 夫妻 本命命宫是 命宫 ``` **边界与陷阱** `palace("soulPalace", "decadal")` 返回的宫位对象上,`name` 仍是**本命宫名**(例中的夫妻), 因为它就是本命盘上的那一格。要看该格在大限层级叫什么,查 `h.decadal.palace_names[index]`。 *** ## surround\_palaces [#surround_palaces] **用途** 取某个运限层级下某宫的三方四正。 **签名** ```python def surround_palaces( self, name: PalaceName | str, scope: Scope | ScopeLiteral, astrolabe: Astrolabe | None = None, ) -> SurroundedPalaces | None ``` **参数** 同 `palace`。 **返回值** `SurroundedPalaces | None`,判断方法见[三方四正](/zh/docs/python/surpalaces)。 **示例** ```python h = chart.horoscope("2025-6-1", 0) sp = h.surround_palaces(PalaceName.WEALTH, Scope.YEARLY) print("流年财帛的三方四正以本命", sp.target.name, "为本宫") ``` **输出** ```text 流年财帛的三方四正以本命 疾厄 为本宫 ``` *** ## has\_horoscope\_stars / has\_one\_of\_horoscope\_stars / not\_have\_horoscope\_stars [#has_horoscope_stars--has_one_of_horoscope_stars--not_have_horoscope_stars] **用途** 判断某层级某宫里有没有指定的流耀。 **斗数含义** 流耀是随运限层级产生的一组星:魁钺昌曲禄羊陀马鸾喜。 它们在不同层级有不同名字——大限层级叫运魁、运钺,流年层级叫流魁、流钺, 含义相同但作用于各自的时间跨度。 **签名** ```python def has_horoscope_stars(self, name, scope, stars: list[str], astrolabe=None) -> bool def has_one_of_horoscope_stars(self, name, scope, stars: list[str], astrolabe=None) -> bool def not_have_horoscope_stars(self, name, scope, stars: list[str], astrolabe=None) -> bool ``` **参数** | 参数 | 类型 | 必填 | 默认 | 说明 | | ----------- | ------------------- | -- | ------ | ------------- | | `name` | `str` | 是 | — | 该层级下的宫名标识 | | `scope` | `str` | 是 | — | 运限层级 | | `stars` | `list[str]` | 是 | — | 流耀标识,须用该层级的名字 | | `astrolabe` | `Astrolabe \| None` | 否 | `None` | 通常不传 | **返回值** | 方法 | 语义 | | ---------------------------- | ----- | | `has_horoscope_stars` | 每一颗都在 | | `has_one_of_horoscope_stars` | 至少一颗在 | | `not_have_horoscope_stars` | 一颗都不在 | **示例** ```python h = chart.horoscope("2025-6-1", 0) print(h.has_horoscope_stars(PalaceName.SOUL, Scope.DECADAL, ["yunlu"])) print(h.has_one_of_horoscope_stars(PalaceName.SOUL, Scope.DECADAL, ["yunlu", "yunyang"])) print(h.not_have_horoscope_stars(PalaceName.SOUL, Scope.DECADAL, ["yuntuo"])) ``` **输出** ```text False False True ``` **边界与陷阱** 三个方法用 `scope` + `name` 定位到本命盘上的某一格, 但要比对的星耀集合恒为**大限流耀与流年流耀的并集**,与 `scope` 无关。 因此 `scope` 传 `"monthly"` 时,查的是「流月某宫这一格里有没有大限或流年的流耀」, 而不是流月自己的流耀——流月、流日、流时三层的流耀不参与这里的比对。 要按层级取流耀分布,用 `h.monthly.stars` 一类字段,或 [`star.get_horoscope_star`](/zh/docs/python/star#get_horoscope_star)。 大限流耀叫 `yunlu`(运禄)、`yunyang`(运羊)……,流年流耀叫 `liulu`(流禄)、 `liuyang`(流羊)……,两组标识不同名。由于比对集合恒是这两组的并集, `yunlu` 与 `liulu` 在任何 `scope` 下都查得到,只是落宫不同。 各层级的标识对照见[安星模块](/zh/docs/python/star#get_horoscope_star), 枚举形式见 `HoroscopeStar`。 *** ## has\_horoscope\_mutagen [#has_horoscope_mutagen] **用途** 判断某层级某宫里有没有该层级天干引发的四化。 **斗数含义** 每个运限层级有自己的天干,会像生年干一样化出四颗星。 「大限化禄落在大限财帛」这类判断问的就是这个。 **签名** ```python def has_horoscope_mutagen(self, name, scope, mutagen: Mutagen, astrolabe=None) -> bool ``` **参数** | 参数 | 类型 | 必填 | 默认 | 说明 | | ----------- | ------------------- | -- | ------ | --------- | | `name` | `str` | 是 | — | 该层级下的宫名标识 | | `scope` | `str` | 是 | — | 运限层级 | | `mutagen` | `str` | 是 | — | 四化标识 | | `astrolabe` | `Astrolabe \| None` | 否 | `None` | 通常不传 | **返回值** `bool`。 **示例** ```python from x_iztro import Mutagen h = chart.horoscope("2025-6-1", 0) print(h.has_horoscope_mutagen(PalaceName.SOUL, Scope.DECADAL, Mutagen.LU)) print(h.decadal.mutagen) ``` **输出** ```text False ['太阳', '武曲', '太阴', '天同'] ``` 大限干为庚,庚干四化为太阳化禄、武曲化权、太阴化科、天同化忌。 **边界与陷阱** 本命层级没有「层级天干」这回事——生年四化已经打在星耀自身的 `mutagen_key` 上。 `has_horoscope_mutagen(name, "origin", m)` 因此直接返回 `False`, 不代表本命盘上没有这个四化。要查本命四化,用宫位的 [`has_mutagen`](/zh/docs/python/palace#has_mutagen--not_have_mutagen)。 只检查目标宫的**主星与辅星**,不看杂耀。 *** ## scope\_item / astrolabe [#scope_item--astrolabe] **用途** 按层级标识取对应的 `HoroscopeItem`,或回到本命盘。 **签名** ```python def scope_item(self, scope: Scope | ScopeLiteral) -> HoroscopeItem | None def astrolabe(self) -> Astrolabe | None ``` **返回值** `scope_item` 在层级为 `"origin"` 时返回 `None`——本命不是运限层级。 **示例** ```python h = chart.horoscope("2025-6-1", 0) print(h.scope_item(Scope.DECADAL).name) print(h.scope_item(Scope.ORIGIN)) print(h.astrolabe().solar_date) ``` **输出** ```text 大限 None 2000-8-16 ``` **边界与陷阱** `scope_item` 用于写按层级参数化的通用逻辑,比一串 `if scope == ...` 简洁。 `HoroscopeItem` 上另有 `palace_index_by_name(name)`, 把宫名在该层级的十二宫里换成宫位索引,查不到返回 `None`: ```python h = chart.horoscope("2025-6-1", 0) item = h.scope_item(Scope.DECADAL) print(item.palace_index_by_name(PalaceName.SOUL)) print(item.palace_index_by_name(PalaceName.WEALTH)) print(item.palace_index_by_name("nosuch")) ``` **输出** ```text 2 10 None ``` *** ## to\_dict / to\_json [#to_dict--to_json] **用途** 把运限导出成与 JS iztro 字段契约一致的 JSON。 **签名** ```python def to_dict(self) -> dict[str, Any] def to_json(self, **kwargs: Any) -> str ``` 形状与用法同[星盘的同名方法](/zh/docs/python/astrolabe#to_dict--to_json): `to_dict` 给底层 DTO 的深拷贝,`to_json` 给 JSON 字符串且默认 `ensure_ascii=False`。 同样不要用 `dataclasses.asdict`——运限持有本命盘的引用,会无限递归。 **示例** ```python h = chart.horoscope("2025-6-1", 0) d = h.to_dict() print(d["solarDate"], d["decadal"]["heavenlyStem"], d["age"]["nominalAge"]) print(sorted(d.keys())) ``` **输出** ```text 2025-6-1 庚 26 ['age', 'daily', 'decadal', 'hourly', 'lunarDate', 'monthly', 'solarDate', 'yearly'] ``` # 轻量查询 (/zh/docs/python/query) 不排整盘就能拿到的生肖、星座与命宫主星。 有些问题不需要整张星盘。这五个函数各自只跑到必要的那一步就返回, 结果与完整排盘的对应字段永远一致——它们走的是同一套核心逻辑。 本页示例统一用默认的 `zh-CN` 排盘,因此输出里的展示值都是中文。 ```python from x_iztro import query ``` *** ## get\_zodiac\_by\_solar\_date [#get_zodiac_by_solar_date] **用途** 由公历日期取生肖。 **斗数含义** 生肖由**年支**决定,而年支的换算时点受 `year_divide` 影响。 正月初一与立春之间出生的人,两种配置会得到不同的生肖——这不是缺陷,是流派差异。 **签名** ```python def get_zodiac_by_solar_date( solar_date: str, language: LanguageType = "zh-CN", config: ChartConfig | None = None, ) -> str ``` **参数** | 参数 | 类型 | 必填 | 默认 | 说明 | | ------------ | --------------------- | -- | --------- | -------------------- | | `solar_date` | `str` | 是 | — | 公历日期,格式 `YYYY-M-D` | | `language` | `str` | 否 | `"zh-CN"` | 输出语言 | | `config` | `ChartConfig \| None` | 否 | `None` | 仅 `year_divide` 影响结果 | **返回值** `str`——按语言翻译的生肖名。 **示例** ```python print(query.get_zodiac_by_solar_date("2000-8-16")) ``` **输出** ```text 龙 ``` **边界与陷阱** 默认按正月初一换年。改成 `ChartConfig(year_divide="exact")` 后按立春换年, 1 月下旬到 2 月上旬出生的人可能拿到不同生肖。 *** ## get\_sign\_by\_solar\_date / get\_sign\_by\_lunar\_date [#get_sign_by_solar_date--get_sign_by_lunar_date] **用途** 取星座。 **斗数含义** 星座是西洋占星概念,只由公历日期决定,与斗数算法无关。 农历版本先把农历转成公历再判定,因此两者对同一天的结果相同。 **签名** ```python def get_sign_by_solar_date(solar_date: str, language: LanguageType = "zh-CN") -> str def get_sign_by_lunar_date( lunar_date: str, is_leap_month: bool = False, language: LanguageType = "zh-CN", ) -> str ``` **参数** | 参数 | 类型 | 必填 | 默认 | 说明 | | --------------------------- | ------ | -- | --------- | ---------------- | | `solar_date` / `lunar_date` | `str` | 是 | — | 日期,格式 `YYYY-M-D` | | `is_leap_month` | `bool` | 否 | `False` | 仅农历版本:该月是否闰月 | | `language` | `str` | 否 | `"zh-CN"` | 输出语言 | 无 `config` 参数——星座不受任何配置影响。 **返回值** `str`。 **示例** ```python print(query.get_sign_by_solar_date("2000-8-16")) print(query.get_sign_by_lunar_date("2000-7-17")) ``` **输出** ```text 狮子座 狮子座 ``` *** ## get\_major\_star\_by\_solar\_date / get\_major\_star\_by\_lunar\_date [#get_major_star_by_solar_date--get_major_star_by_lunar_date] **用途** 只取命宫主星,不排整盘。 **斗数含义** 命宫主星是斗数最常被单独问起的一项。 命宫为空宫时按惯例借对宫主星来看,本函数已经处理了这一步。 **签名** ```python def get_major_star_by_solar_date( solar_date: str, time_index: TimeIndexType, *, fix_leap: bool = True, language: LanguageType = "zh-CN", config: ChartConfig | None = None, ) -> str def get_major_star_by_lunar_date( lunar_date: str, time_index: TimeIndexType, *, is_leap_month: bool = False, fix_leap: bool = True, language: LanguageType = "zh-CN", config: ChartConfig | None = None, ) -> str ``` **参数** | 参数 | 类型 | 必填 | 默认 | 说明 | | --------------------------- | --------------------- | -- | --------- | ------------------------------------ | | `solar_date` / `lunar_date` | `str` | 是 | — | 日期 | | `time_index` | `int` | 是 | — | 时辰索引 0–12,命宫由月份与时辰共同决定。之后的参数只能按关键字传入 | | `is_leap_month` | `bool` | 否 | `False` | 仅农历版本 | | `fix_leap` | `bool` | 否 | `True` | 是否修正闰月 | | `language` | `str` | 否 | `"zh-CN"` | 输出语言 | | `config` | `ChartConfig \| None` | 否 | `None` | 排盘配置 | **返回值** `str`——多颗主星以逗号分隔;空宫时返回对宫主星。 **示例** ```python print(query.get_major_star_by_solar_date("2000-8-16", 2)) print(query.get_major_star_by_solar_date("2000-8-16", 2, language="en-US")) ``` **输出** ```text 紫微 emperor ``` **边界与陷阱** 命宫由农历月份与出生时辰共同定位,因此 `time_index` 是必填的。 只知道日期不知道时辰时,斗数无法给出确定的命宫。 返回值是翻译后的字符串,换语言就会变。要做程序判断请排整盘, 用 `chart.palace("soulPalace")` 取宫位后比较 `major_stars` 里的 `key`。 # 工具函数 (/zh/docs/python/util) 索引换算、亮度与四化查表、命身宫推算、大限小限、四柱展示串。 这些函数是排盘算法的零件。自己实现斗数逻辑、或要复核某一步推算时用得上; 日常排盘不必直接调用。 ```python from x_iztro import utils ``` 参数与返回值中的标识都与语言无关,可直接与星盘上的 `*_key` 字段互操作。 返回结构体的函数给的是**具名 dataclass**,字段用属性访问; 返回单个标识的函数给的是枚举成员(`StrEnum`,与等值字符串可直接比较)。 *** ## fix\_index [#fix_index] **用途** 把任意整数约束到 `0..max` 的循环区间。 **斗数含义** 十二宫首尾相接,从丑宫(索引 11)再走一格回到寅宫(索引 0)。 所有「顺数几格、逆数几格」的推算都靠这个回绕。 **签名** ```python def fix_index(index: int, max: int = 12) -> int ``` **参数** | 参数 | 类型 | 必填 | 默认 | 说明 | | ------- | ----- | -- | ---- | ----------- | | `index` | `int` | 是 | — | 待修正的索引,可为负 | | `max` | `int` | 否 | `12` | 循环长度,天干用 10 | **返回值** `int`,落在 `0..max`——含 0,**不含 `max`** 本身。 **示例** ```python print(utils.fix_index(-1), utils.fix_index(13)) ``` **输出** ```text 11 1 ``` **边界与陷阱** 负数按数学取模回绕(-1 → 11),不是截断到 0。`max` 传 0 会抛 `ZeroDivisionError`,调用方自己保证它是正数——盘上的用法固定为 12 或 10。 *** ## earthly\_branch\_to\_palace\_index [#earthly_branch_to_palace_index] **用途** 地支转宫位索引。 **斗数含义** 十二宫的排列从**寅宫**起,而地支的自然顺序从**子**起,两者差两格。 这个函数负责这层换算:寅 → 0,卯 → 1,⋯,子 → 10,丑 → 11。 **签名** ```python def earthly_branch_to_palace_index(branch: EarthlyBranch | str) -> int ``` **返回值** `int`,0–11。 **示例** ```python from x_iztro import EarthlyBranch print(utils.earthly_branch_to_palace_index(EarthlyBranch.YIN)) print(utils.earthly_branch_to_palace_index(EarthlyBranch.ZI)) ``` **输出** ```text 0 10 ``` *** ## time\_to\_index [#time_to_index] **用途** 小时数转时辰索引。 **斗数含义** 一天十二时辰,每时辰两小时,但子时横跨午夜被拆成早子时(0)与晚子时(12), 因此索引有 13 个值。 **签名** ```python def time_to_index(hour: int) -> int ``` **参数** | 参数 | 类型 | 必填 | 默认 | 说明 | | ------ | ----- | -- | -- | -------- | | `hour` | `int` | 是 | — | 小时数 0–23 | **返回值** `int`,0–12。 **示例** ```python print(utils.time_to_index(0), utils.time_to_index(4), utils.time_to_index(23)) ``` **输出** ```text 0 2 12 ``` 0 点为早子时,4 点为寅时,23 点为晚子时。排盘时不确定时辰索引,用这个函数换算。 *** ## get\_age\_index [#get_age_index] **用途** 由生年地支取小限起始宫位索引。 **斗数含义** 小限从固定的宫起,按虚岁逐年推移。起宫由生年地支所属的三合组决定: 寅午戌年起辰宫、申子辰年起戌宫、巳酉丑年起未宫、亥卯未年起丑宫。 **签名** ```python def get_age_index(branch: EarthlyBranch | str) -> int ``` **返回值** `int`,0–11。 **示例** ```python print(utils.get_age_index("chenEarthly")) ``` **输出** ```text 8 ``` 辰年属申子辰组,小限从戌宫起,戌宫的索引是 8。 *** ## get\_brightness [#get_brightness] **用途** 查某颗星落在某宫时的亮度。 **签名** ```python def get_brightness( star: str, palace_index: int, config: ChartConfig | None = None, ) -> Brightness | None ``` **参数** | 参数 | 类型 | 必填 | 默认 | 说明 | | -------------- | --------------------- | -- | ------ | --------------- | | `star` | `str` | 是 | — | 星耀标识 | | `palace_index` | `int` | 是 | — | 宫位索引,越界会对 12 取模 | | `config` | `ChartConfig \| None` | 否 | `None` | 自定义亮度表会改变结果 | **返回值** `Brightness` 枚举成员;该星没有亮度表时返回 `None`。 它是 `StrEnum`,`utils.get_brightness("ziweiMaj", 4) == "miao"` 成立。 星耀标识未知时抛 `IztroError`(`code` 为 `invalid_argument`)。 **示例** ```python print(utils.get_brightness("ziweiMaj", 4)) print(utils.get_brightness("lucunMin", 0)) ``` **输出** ```text miao None ``` 紫微在午宫(索引 4)庙;禄存没有亮度表。 *** ## get\_mutagen / get\_mutagens\_by\_heavenly\_stem [#get_mutagen--get_mutagens_by_heavenly_stem] **用途** 查天干四化。 **斗数含义** 十天干各自固定指派四颗星化禄、权、科、忌。 `get_mutagen` 问「这颗星在这个天干下化什么」, `get_mutagens_by_heavenly_stem` 问「这个天干化哪四颗星」。 **签名** ```python def get_mutagen(star: str, stem: HeavenlyStem | str, config: ChartConfig | None = None) -> Mutagen | None def get_mutagens_by_heavenly_stem(stem: HeavenlyStem | str, config: ChartConfig | None = None) -> list[str] ``` **返回值** `get_mutagen` 返回 `Mutagen` 枚举成员,该星不在此天干的四化表内时为 `None`。 `get_mutagens_by_heavenly_stem` 返回四项星耀标识列表(`list[str]`),顺序为**禄、权、科、忌**。 两者都受 `config` 里的自定义四化表影响。 **示例** ```python print(utils.get_mutagen("taiyangMaj", "gengHeavenly")) print(utils.get_mutagen("ziweiMaj", "gengHeavenly")) print(utils.get_mutagens_by_heavenly_stem("gengHeavenly")) ``` **输出** ```text sihuaLu None ['taiyangMaj', 'wuquMaj', 'taiyinMaj', 'tiantongMaj'] ``` *** ## get\_soul\_and\_body [#get_soul_and_body] **用途** 由农历月索引、时辰与年干推命宫、身宫。 **斗数含义** 命宫是整张盘的起点:从寅宫起正月,顺数到生月,再从生月逆数到生时。 身宫用同样的起点但顺数生时。命宫的天干由五虎遁从年干推得。 **签名** ```python def get_soul_and_body( month_index: int, time_index: int, yearly_stem: HeavenlyStem | str, ) -> SoulAndBody ``` **参数** | 参数 | 类型 | 必填 | 默认 | 说明 | | ------------- | ----- | -- | -- | ---------------------------------------- | | `month_index` | `int` | 是 | — | 农历月索引,正月为 0;由 `fix_lunar_month_index` 求得 | | `time_index` | `int` | 是 | — | 时辰索引 0–12 | | `yearly_stem` | `str` | 是 | — | 生年天干标识 | **返回值** `SoulAndBody`: | 字段 | 类型 | 说明 | | ------------------------ | ----- | ------ | | `soul_index` | `int` | 命宫宫位索引 | | `body_index` | `int` | 身宫宫位索引 | | `heavenly_stem_of_soul` | `str` | 命宫天干标识 | | `earthly_branch_of_soul` | `str` | 命宫地支标识 | **示例** ```python sb = utils.get_soul_and_body(6, 2, "gengHeavenly") print(sb) print(sb.soul_index, sb.body_index, sb.earthly_branch_of_soul) ``` **输出** ```text SoulAndBody(soul_index=4, body_index=8, heavenly_stem_of_soul='renHeavenly', earthly_branch_of_soul='wuEarthly') 4 8 wuEarthly ``` *** ## get\_five\_elements\_class [#get_five_elements_class] **用途** 由命宫干支推五行局。 **斗数含义** 五行局(水二、木三、金四、土五、火六)决定两件大事: 紫微星的起宫位置,以及大限的起运岁数。 **签名** ```python def get_five_elements_class(stem: HeavenlyStem | str, branch: EarthlyBranch | str) -> str ``` **返回值** 五行局标识字符串(`FiveElementsClass` 的值域)。 **示例** ```python print(utils.get_five_elements_class("renHeavenly", "wuEarthly")) ``` **输出** ```text wood3rd ``` *** ## get\_palace\_names [#get_palace_names] **用途** 由命宫索引推十二宫名。 **斗数含义** 命宫定下后,其余十一宫按固定顺序逆时针排开: 命、兄弟、夫妻、子女、财帛、疾厄、迁移、仆役、官禄、田宅、福德、父母。 **签名** ```python def get_palace_names(soul_index: int) -> list[PalaceName] ``` **返回值** 十二项 `PalaceName` 列表,**按宫位索引排列**——第 `i` 项就是 `chart.palaces[i]` 的宫名。 **示例** ```python names = utils.get_palace_names(4) print(names[:4]) print([str(n) for n in names[:4]]) print(names[0] == "wealthPalace") ``` **输出** ```text [, , , ] ['wealthPalace', 'childrenPalace', 'spousePalace', 'siblingsPalace'] True ``` 列表元素是 `PalaceName` 枚举成员,`repr` 带枚举名、`str` 给标识本身; 因为是 `StrEnum`,与字符串直接比较也成立。 命宫在索引 4,因此索引 0(寅宫)是财帛。 *** ## get\_decadals\_and\_ages [#get_decadals_and_ages] **用途** 由命宫索引与五行局推十二宫的大限与小限。 **斗数含义** 大限起运岁数由五行局决定(水二局 2 岁起、木三局 3 岁起,依此类推), 顺逆由性别阴阳与年支阴阳决定;小限起宫由年支决定,按虚岁逐年推移。 **签名** ```python def get_decadals_and_ages( soul_index: int, five_elements_class: str, gender: str, yearly_stem: HeavenlyStem | str, yearly_branch: EarthlyBranch | str, ) -> DecadalsAndAges ``` **参数** | 参数 | 类型 | 必填 | 默认 | 说明 | | --------------------- | ----- | -- | -- | --------------------- | | `soul_index` | `int` | 是 | — | 命宫宫位索引 | | `five_elements_class` | `str` | 是 | — | 五行局标识 | | `gender` | `str` | 是 | — | `"male"` 或 `"female"` | | `yearly_stem` | `str` | 是 | — | 年干标识 | | `yearly_branch` | `str` | 是 | — | 年支标识 | **返回值** `DecadalsAndAges`,两个字段都按宫位索引排列: | 字段 | 类型 | 说明 | | ---------- | ----------------- | ------------ | | `decadals` | `list[Decadal]` | 十二宫各自的大限 | | `ages` | `list[list[int]]` | 十二宫各自的小限虚岁列表 | `Decadal` 与宫位上的 `palace.decadal` 是同一个类型: | 字段 | 类型 | 说明 | | --------------------------------------- | ----------------- | ------------ | | `range` | `tuple[int, int]` | 大限起止虚岁,含两端 | | `heavenly_stem` / `heavenly_stem_key` | `str` | 大限天干的译名 / 标识 | | `earthly_branch` / `earthly_branch_key` | `str` | 大限地支的译名 / 标识 | **示例** ```python d = utils.get_decadals_and_ages(4, "wood3rd", "female", "gengHeavenly", "chenEarthly") print(d.decadals[0]) print(d.decadals[0].range, d.decadals[0].earthly_branch_key) print(d.ages[0][:3]) ``` **输出** ```text Decadal(range=(43, 52), heavenly_stem='戊', heavenly_stem_key='wuHeavenly', earthly_branch='寅', earthly_branch_key='yinEarthly') (43, 52) yinEarthly [9, 21, 33] ``` `Decadal` 的译名字段按 **zh-CN** 生成——这个函数不收 `language` 参数。 要别的语言用 `heavenly_stem_key` 走 [`i18n.translate`](/zh/docs/python/i18n#translate)。 **边界与陷阱** 整盘排出的每个宫位上已有 `decadal` 与 `ages` 字段,内容与本函数一致。 这个函数用于不排整盘、只推大限小限的场合。 *** ## fix\_lunar\_month\_index / fix\_lunar\_day\_index [#fix_lunar_month_index--fix_lunar_day_index] **用途** 求修正后的农历月索引与日索引。 **斗数含义** 闰月归属与晚子时归属是斗数两个长期有争议的边界,这两个函数把规则落定: 闰月十六日起按下月算(可关,且晚子时不进位),晚子时的日索引属次日。 **签名** ```python def fix_lunar_month_index( lunar_month: int, lunar_day: int, is_leap: bool, time_index: int, fix_leap: bool, ) -> int def fix_lunar_day_index(lunar_day: int, time_index: int) -> int ``` **返回值** 月索引为 0-based(正月为 0);日索引在晚子时不减一。 `fix_lunar_month_index` 进位要同时满足四个条件:`is_leap` 为真、`fix_leap` 为真、 `lunar_day` 大于 15、且 `time_index` 不是 12。四者缺一,就按本月算。 **示例** ```python print(utils.fix_lunar_month_index(7, 17, False, 2, True)) print(utils.fix_lunar_day_index(17, 2), utils.fix_lunar_day_index(17, 12)) ``` **输出** ```text 6 16 17 ``` 七月非闰月,索引为 6;十七日在寅时减一得 16,在晚子时属次日故保持 17。 *** ## translate\_chinese\_date [#translate_chinese_date] **用途** 把四柱干支拼成展示串。 **签名** ```python def translate_chinese_date( pillars: list[tuple[str, str]], language: str = "zh-CN", ) -> str ``` **参数** | 参数 | 类型 | 必填 | 默认 | 说明 | | ---------- | ----------------------- | -- | --------- | ------------------------------- | | `pillars` | `list[tuple[str, str]]` | 是 | — | 四柱标识 \[年, 月, 日, 时],每柱为 (天干, 地支) | | `language` | `str` | 否 | `"zh-CN"` | 输出语言 | **返回值** `str`。词条均为单字符时柱内紧凑相连、柱间空格; 任一词条为多字符时柱内空格、柱间 `-`。 **示例** ```python pillars = [ ("gengHeavenly", "chenEarthly"), ("jiaHeavenly", "shenEarthly"), ("bingHeavenly", "wuEarthly"), ("gengHeavenly", "yinEarthly"), ] print(utils.translate_chinese_date(pillars)) # 星盘上的四柱标识可直接取 print(utils.translate_chinese_date(chart.raw_dates.chinese_date.pillar_keys())) ``` **输出** ```text 庚辰 甲申 丙午 庚寅 庚辰 甲申 丙午 庚寅 ``` **边界与陷阱** 柱数不为四、某柱不是两项,或干支标识非法时抛 `IztroError`(`code` 为 `invalid_argument`)。 *** ## merge\_stars [#merge_stars] **用途** 把多组「十二宫星耀」按宫位合并成一组。 **斗数含义** 安星是分批进行的:主星、辅星、杂耀各出一组十二宫列表。 要把它们并成一张完整盘面时用这个函数。 **签名** ```python def merge_stars(*groups: list[list[Star]]) -> list[list[Star]] ``` **参数** | 参数 | 类型 | 必填 | 默认 | 说明 | | -------- | ------------------ | -- | -- | ------------------------------------------------------------------------- | | `groups` | `list[list[Star]]` | 是 | — | 若干组十二宫星耀列表,每组长度须为 12。注意是**可变参数**:写 `merge_stars(major, minor)`,不是传一个列表的列表 | **返回值** 合并后的十二宫列表,同宫内按传入顺序首尾相接。 **示例** ```python from x_iztro import star major = star.get_major_star("2000-8-16", 2, "female") minor = star.get_minor_star("2000-8-16", 2, "female") merged = utils.merge_stars(major, minor) print([s.name for s in merged[0]]) ``` **输出** ```text ['武曲', '天相', '天马'] ``` **边界与陷阱** 某一组的长度不是 12 时抛 `ValueError`。这是纯本地实现,不经绑定层。 # 安星模块 (/zh/docs/python/star) 按出生数据取某一组星耀的落宫。 不排整盘、只想知道「禄存落在哪一宫」或「这张盘的杂耀怎么分布」时用这一层。 ```python from x_iztro import star ``` 所有索引都是**宫位索引**:0 为寅宫,11 为丑宫。 ## 共用参数 [#共用参数] 按出生数据安星的入口共用同一组参数: | 参数 | 类型 | 必填 | 默认 | 说明 | | --------------------------- | --------------------- | -- | --------- | ------------------ | | `solar_date` | `str` | 是 | — | 公历日期,格式 `YYYY-M-D` | | `time_index` | `int` | 是 | — | 时辰索引 0–12 | | `gender` | `str` | 否 | `"male"` | 性别,决定长生与博士十二神的顺逆 | | `fix_leap` | `bool` | 否 | `True` | 是否修正闰月 | | `language` | `str` | 否 | `"zh-CN"` | 星耀名称的输出语言 | | `config` | `ChartConfig \| None` | 否 | `None` | 排盘配置 | | `from_stem` / `from_branch` | `str \| None` | 否 | `None` | 起五行局的干支;两者须同时给出 | ```python birth = dict(solar_date="2000-8-16", time_index=2, gender="female") ``` 本页示例统一用默认的 `zh-CN`,因此星名输出都是中文。 各入口的返回值都是**具名 dataclass**(不是 dict),字段用属性访问: `star.get_start_index(**birth).ziwei_index`。 两者同时给出后,五行局改由该干支推算,进而改变紫微天府落点与长生十二神。 其余各组星的起法不受影响。用它可以取到中州派地盘、人盘的安星结果。 支持这两个参数的只有 `get_start_index`、`get_major_star`、`get_changsheng12`。 *** ## get\_start\_index [#get_start_index] **用途** 求紫微、天府的起始宫位。 **斗数含义** 紫微是全盘的锚点:由五行局与农历生日按「起紫微星诀」定位, 其余十三颗主星再依紫微与天府的位置铺开。天府与紫微的位置互为镜像。 **签名** ```python def get_start_index(solar_date, time_index, gender="male", fix_leap=True, language="zh-CN", config=None, from_stem=None, from_branch=None) -> dict[str, int] ``` **返回值** `StartIndex`,字段 `ziwei_index`、`tianfu_index`。 **示例** ```python s = star.get_start_index(**birth) print(s) print(s.ziwei_index, s.tianfu_index) ``` **输出** ```text StartIndex(ziwei_index=4, tianfu_index=8) 4 8 ``` *** ## 各组落宫索引 [#各组落宫索引] 以下六个入口形状一致:收出生数据,返回一个字段全是宫位索引的 dataclass。 | 函数 | 返回类型 | 字段 | 起法依据 | | -------------------------- | ------------------ | ---------------------------------------------- | ----------------- | | `get_lu_yang_tuo_ma_index` | `LuYangTuoMaIndex` | `lu_index` `yang_index` `tuo_index` `ma_index` | 年干定禄存,禄前羊后陀;天马按年支 | | `get_kui_yue_index` | `KuiYueIndex` | `kui_index` `yue_index` | 年干 | | `get_chang_qu_index` | `ChangQuIndex` | `chang_index` `qu_index` | 时支 | | `get_kong_jie_index` | `KongJieIndex` | `kong_index` `jie_index` | 时支 | | `get_timely_star_index` | `TimelyStarIndex` | `taifu_index` `fenggao_index` | 时支 | | `get_luan_xi_index` | `LuanXiIndex` | `hongluan_index` `tianxi_index` | 年支 | **示例** ```python print(star.get_lu_yang_tuo_ma_index(**birth)) print(star.get_chang_qu_index(**birth)) print(star.get_luan_xi_index(**birth)) ``` **输出** ```text LuYangTuoMaIndex(lu_index=6, yang_index=7, tuo_index=5, ma_index=0) ChangQuIndex(chang_index=6, qu_index=4) LuanXiIndex(hongluan_index=9, tianxi_index=3) ``` 擎羊在禄存前一格、陀罗在后一格,这是「禄前羊刃当,禄后陀罗府」的直接体现。 *** ## get\_daily\_star\_index / get\_monthly\_star\_index / get\_yearly\_star\_index [#get_daily_star_index--get_monthly_star_index--get_yearly_star_index] **用途** 取按日、按月、按年起的杂耀落宫。 **斗数含义** 杂耀按起法分组:日系星从辅星位置起初一顺数到生日; 月系星按农历月份定位;年系星最多,按年干或年支起。 **返回值** | 函数 | 返回类型 | 字段 | | ------------------------ | ------------------ | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | | `get_daily_star_index` | `DailyStarIndex` | `santai_index` `bazuo_index` `enguang_index` `tiangui_index` | | `get_monthly_star_index` | `MonthlyStarIndex` | `yuejie_index`(解神) `tianyao_index` `tianxing_index` `yinsha_index` `tianyue_index` `tianwu_index` | | `get_yearly_star_index` | `YearlyStarIndex` | 27 项:`xianchi_index` `huagai_index` `guchen_index` `guasu_index` `tiancai_index` `tianshou_index` `tianchu_index` `posui_index` `feilian_index` `longchi_index` `fengge_index` `tianku_index` `tianxu_index` `tianguan_index` `tianfu_index` `tiande_index` `yuede_index` `tiankong_index` `jielu_index` `kongwang_index` `xunkong_index` `tianshang_index` `tianshi_index` `jiekong_index` `jiesha_adj_index` `nianjie_index` `dahao_adj_index` | 红鸾、天喜也属年系,但不在 `YearlyStarIndex` 里——它们由 `get_luan_xi_index` 单独给出。 **示例** ```python d = star.get_daily_star_index(**birth) m = star.get_monthly_star_index(**birth) y = star.get_yearly_star_index(**birth) print(d) print(m.yuejie_index, m.tianyao_index, m.tianxing_index) print(y.xianchi_index, y.huagai_index, y.tianshang_index, y.tianshi_index) ``` **输出** ```text DailyStarIndex(santai_index=0, bazuo_index=10, enguang_index=9, tiangui_index=7) 0 5 1 7 2 9 11 ``` **边界与陷阱** 年系杂耀属流年神煞,取年支时用的是 `horoscope_divide` 而非 `year_divide`。 两个配置不同时,年系星与主星、辅星可能基于不同的年支——这是刻意的流派区分。 这三项只在 `algorithm` 为中州派时进入盘面,替换掉截路、空亡与大耗的默认取法; 默认派别下它们仍会被算出来,只是不安进宫位。 *** ## get\_major\_star / get\_minor\_star / get\_adjective\_star [#get_major_star--get_minor_star--get_adjective_star] **用途** 取主星、辅星、杂耀在十二宫的完整分布。 **签名** ```python def get_major_star(...) -> list[list[Star]] def get_minor_star(...) -> list[list[Star]] def get_adjective_star(...) -> list[list[Star]] ``` **返回值** 十二项列表,按宫位索引排列。每项是该宫的 `Star` 列表(可能为空)。 **示例** ```python major = star.get_major_star(**birth) for i, stars in enumerate(major[:5]): print(i, [s.name for s in stars]) ``` **输出** ```text 0 ['武曲', '天相'] 1 ['太阳', '天梁'] 2 ['七杀'] 3 ['天机'] 4 ['紫微'] ``` **边界与陷阱** 返回的 `Star` 带亮度与生年四化标记,与整盘排出的完全一致—— 它们走的是同一段代码。要取整盘的话直接用 `Astro().by_solar(...)` 更省事。 *** ## get\_changsheng12 / get\_boshi12 / get\_yearly12 [#get_changsheng12--get_boshi12--get_yearly12] **用途** 取四组十二神在十二宫的排列。 **斗数含义** 这四组各是十二个标记排满十二宫,每宫恰好一个: 长生十二神按五行局起、随性别与年支阴阳定顺逆; 博士十二神从禄存起、同样定顺逆; 岁前十二神从年支起顺行;将前十二神按年支三合组起。 **签名** ```python def get_changsheng12(...) -> list[str] def get_boshi12(...) -> list[str] def get_yearly12(...) -> dict[str, list[str]] ``` **返回值** `get_changsheng12` 与 `get_boshi12` 返回十二项标识列表,按宫位索引排列。 `get_yearly12` 返回 `Yearly12`,字段 `suiqian12`、`jiangqian12` 各是一个十二项标识列表。 **示例** ```python print(star.get_changsheng12(**birth)[:4]) print(star.get_boshi12(**birth)[:4]) y = star.get_yearly12(**birth) print(y.suiqian12[:4]) print(y.jiangqian12[:4]) ``` **输出** ```text ['jue', 'mu', 'si', 'bing'] ['faylian', 'zhoushu', 'jiangjun', 'xiaohao'] ['diaoke', 'bingfu', 'suijian', 'huiqi'] ['suiyi', 'xiishen', 'huagai', 'jiesha'] ``` 返回的是标识而非译名,要展示用 `i18n.translate(key)`。 *** ## get\_changsheng12\_start\_index / get\_jiangqian12\_start\_index [#get_changsheng12_start_index--get_jiangqian12_start_index] **用途** 只取两组十二神的起始宫位,不排整组。 **斗数含义** 长生起点由五行局定:水二局长生在申、木三局在亥、金四局在巳、 土五局在申、火六局在寅。将星起点由年支三合组定:寅午戌年在午、申子辰年在子、 巳酉丑年在酉、亥卯未年在卯。 **签名** ```python def get_changsheng12_start_index(five_elements_class: FiveElementsClass | str) -> int def get_jiangqian12_start_index(branch: EarthlyBranch | str) -> int ``` **返回值** `int`,0–11。这两个函数不需要出生数据。 **示例** ```python print(star.get_changsheng12_start_index("water2nd"), star.get_changsheng12_start_index("fire6th")) print(star.get_jiangqian12_start_index("ziEarthly"), star.get_jiangqian12_start_index("wuEarthly")) ``` **输出** ```text 6 0 10 4 ``` 水二局长生在申(索引 6),火六局在寅(索引 0)。 *** ## get\_horoscope\_star [#get_horoscope_star] **用途** 取某个运限层级的流耀分布。 **斗数含义** 流耀是随运限产生的十颗星:魁钺昌曲禄羊陀马鸾喜。 它们的落宫由该层级的干支决定,名字随层级变化。流年层级额外多一颗年解。 **签名** ```python def get_horoscope_star( stem: HeavenlyStem | str, branch: EarthlyBranch | str, scope: Scope | str, language: str = "zh-CN", ) -> list[list[Star]] ``` **参数** | 参数 | 类型 | 必填 | 默认 | 说明 | | ---------- | ----- | -- | --------- | --------- | | `stem` | `str` | 是 | — | 该层级的天干标识 | | `branch` | `str` | 是 | — | 该层级的地支标识 | | `scope` | `str` | 是 | — | 运限层级,决定星名 | | `language` | `str` | 否 | `"zh-CN"` | 输出语言 | **返回值** 十二项列表,按宫位索引排列。 **各层级的星名对照** | 本命 | 大限 | 流年 | 流月 | 流日 | 流时 | | -- | -- | -- | -- | -- | -- | | 天魁 | 运魁 | 流魁 | 月魁 | 日魁 | 时魁 | | 天钺 | 运钺 | 流钺 | 月钺 | 日钺 | 时钺 | | 文昌 | 运昌 | 流昌 | 月昌 | 日昌 | 时昌 | | 文曲 | 运曲 | 流曲 | 月曲 | 日曲 | 时曲 | | 禄存 | 运禄 | 流禄 | 月禄 | 日禄 | 时禄 | | 擎羊 | 运羊 | 流羊 | 月羊 | 日羊 | 时羊 | | 陀罗 | 运陀 | 流陀 | 月陀 | 日陀 | 时陀 | | 天马 | 运马 | 流马 | 月马 | 日马 | 时马 | | 红鸾 | 运鸾 | 流鸾 | 月鸾 | 日鸾 | 时鸾 | | 天喜 | 运喜 | 流喜 | 月喜 | 日喜 | 时喜 | 标识形如 `yunlu`(运禄)、`liulu`(流禄)、`yuelu`(月禄)、`rilu`(日禄)、`shilu`(时禄)。 **示例** ```python decadal = star.get_horoscope_star("jiaHeavenly", "ziEarthly", "decadal") print([[s.name for s in p] for p in decadal[:4]]) origin = star.get_horoscope_star("jiaHeavenly", "ziEarthly", "origin") print([[s.name for s in p] for p in origin[:2]]) ``` **输出** ```text [['运禄', '运马'], ['运羊', '运鸾'], [], ['运昌']] [['禄存', '天马'], ['擎羊', '红鸾']] ``` **边界与陷阱** `"yearly"` 的结果里额外含年解,按流年地支定位,安放在十颗流耀之前。 其余层级没有这一颗。 *** ## 低层落宫 [#低层落宫] 上面的函数都从出生数据起算,内部先推出年干支、命宫、修正后的农历月,再落宫。 这一组则直接收那些中间量,自建流程时可以复用。 | 函数 | 收 | 出 | | ---------------------------------------------------------------------- | ------------ | ------------------------------------------------------- | | `get_zuo_you_index(lunar_month)` | 修正后的农历月 1–12 | `ZuoYouIndex(zuo_index, you_index)` | | `get_huo_ling_index(branch, time_index)` | 年支、时辰 | `HuoLingIndex(huo_index, ling_index)` | | `get_huagai_xianchi_index(branch)` | 年支 | `HuagaiXianchiIndex(huagai_index, xianchi_index)` | | `get_gu_gua_index(branch)` | 年支 | `GuGuaIndex(guchen_index, guasu_index)` | | `get_jiesha_adj_index(branch)` | 年支 | `int`,劫煞宫位索引 | | `get_dahao_index(branch)` | 年支 | `int`,大耗宫位索引 | | `get_nianjie_index(branch)` | 年支 | `int`,年解宫位索引 | | `get_tianshi_tianshang_index(gender, branch, soul_index, config=None)` | 性别、年支、命宫索引 | `TianshiTianshangIndex(tianshang_index, tianshi_index)` | | `get_chang_qu_index_by_heavenly_stem(stem)` | 天干 | `ChangQuIndex(chang_index, qu_index)` | **示例** ```python from x_iztro import star chart = Astro().by_solar("2000-8-16", 2, "female") year_branch = chart.raw_dates.chinese_date.yearly_keys[1] print(star.get_huo_ling_index(year_branch, 2)) print(star.get_gu_gua_index(year_branch)) print(star.get_chang_qu_index_by_heavenly_stem("jiaHeavenly")) ``` **输出** ```text HuoLingIndex(huo_index=2, ling_index=10) GuGuaIndex(guchen_index=3, guasu_index=11) ChangQuIndex(chang_index=3, qu_index=7) ``` **边界与陷阱** `get_zuo_you_index` 收的是修正闰月之后的月份,即 `fix_lunar_month_index(...) + 1`, 不是农历原始月份。闰月盘直接传原始月份会落错宫。 `get_tianshi_tianshang_index` 的结果随 `config.algorithm` 变:中州派在阴男阳女 (生年地支阴阳与性别阴阳不同)时天伤天使对调,通行派不对调。 `get_chang_qu_index_by_heavenly_stem` 按天干起昌曲,用于运限层级的流昌流曲; 本命盘的文昌文曲按时支走 `get_chang_qu_index`。 # 数据表 (/zh/docs/python/data) 星耀基础信息、天干地支信息、顺序常量与全部枚举。 排盘算法的输入表与语言无关标识的枚举清单。 ```python from x_iztro import data ``` 四个入口返回的都是**具名 dataclass**(外层容器仍是 `dict` / `list`), 字段用属性访问而不是下标。字段名是 snake\_case,与底层 JSON 的 camelCase 键不同。 *** ## stars\_info [#stars_info] **用途** 取星耀基础信息表。 **签名** ```python def stars_info() -> dict[str, StarInfo] ``` **返回值** 星耀标识 → `StarInfo`。 只有二十颗星有记录:**十四主星**加文昌、文曲、火星、铃星、擎羊、陀罗。 | 字段 | 类型 | 说明 | | --------------- | ------------- | -------------------------- | | `brightness` | `list[str]` | 十二宫亮度标识,索引 0 为寅宫;该宫无亮度则为空串 | | `five_elements` | `str \| None` | 五行 | | `yin_yang` | `str \| None` | 阴阳 | **示例** ```python info = data.stars_info() print(len(info)) print(info["ziweiMaj"]) print(info["taiyangMaj"].five_elements) ``` **输出** ```text 20 StarInfo(brightness=['wang', 'wang', 'de', 'wang', 'miao', 'miao', 'wang', 'wang', 'de', 'wang', 'ping', 'miao'], five_elements='土', yin_yang='阴') None ``` **边界与陷阱** 表中部分星耀的五行或阴阳未填:太阳与七杀两项皆为 `None`, 贪狼、天相、天梁、破军的阴阳为 `None`,六颗辅星两项皆为 `None`。 *** ## heavenly\_stems [#heavenly_stems] **用途** 取天干信息表。 **斗数含义** 天干的四化表是四化系统的根:生年干决定生年四化, 宫干决定该宫飞出的四化,运限干决定该层级的四化。 **签名** ```python def heavenly_stems() -> dict[str, HeavenlyStemInfo] ``` **返回值** 天干标识 → `HeavenlyStemInfo`: | 字段 | 类型 | 说明 | | --------------- | ------------- | ----------------- | | `yin_yang` | `str` | 阴阳 | | `five_elements` | `str` | 五行 | | `crash` | `str \| None` | 对冲天干标识;戊、己无对冲 | | `mutagen` | `list[str]` | 四化四星标识,顺序为禄、权、科、忌 | **示例** ```python stems = data.heavenly_stems() print(stems["jiaHeavenly"]) print(stems["wuHeavenly"].crash) ``` **输出** ```text HeavenlyStemInfo(yin_yang='阳', five_elements='木', crash='gengHeavenly', mutagen=['lianzhenMaj', 'pojunMaj', 'wuquMaj', 'taiyangMaj']) None ``` 这是**内置默认表**。自定义四化表(`ChartConfig(mutagens=...)`)不会反映在这里; 要看某张盘上实际生效的四化,用 [`utils.get_mutagens_by_heavenly_stem(stem, config)`](/zh/docs/python/util#get_mutagen--get_mutagens_by_heavenly_stem) 或宫位的 `mutagen_star_keys`。 *** ## earthly\_branches [#earthly_branches] **用途** 取地支信息表。 **签名** ```python def earthly_branches() -> dict[str, EarthlyBranchInfo] ``` **返回值** 地支标识 → `EarthlyBranchInfo`: | 字段 | 类型 | 说明 | | --------------- | ----- | ---------------- | | `yin_yang` | `str` | 阴阳,决定大限与长生十二神的顺逆 | | `five_elements` | `str` | 五行 | | `crash` | `str` | 对冲地支标识 | | `soul` | `str` | 命主星标识(按命宫地支查) | | `body` | `str` | 身主星标识(按生年地支查) | | `inside` | `str` | 对应脏腑 | | `outside` | `str` | 对应身体部位 | | `health_tip` | `str` | 健康提示 | `inside` / `outside` / `health_tip` 三项只有中文一种写法,不参与国际化。 **示例** ```python zi = data.earthly_branches()["ziEarthly"] print(zi) print(zi.soul, zi.body, zi.crash) ``` **输出** ```text EarthlyBranchInfo(yin_yang='阳', five_elements='水', crash='wuEarthly', soul='tanlangMaj', body='huoxingMin', inside='胆', outside='下体', health_tip='生殖系统、膀胱、尿道之疾病,听觉障碍') tanlangMaj huoxingMin wuEarthly ``` *** ## constants [#constants] **用途** 取顺序常量与推算规则表。 **签名** ```python def constants() -> Constants ``` **返回值** `Constants`: | 字段 | 类型 | 说明 | | --------------------- | ---------------- | -------------------------- | | `languages` | `list[str]` | 支持的语言代码 | | `heavenly_stems` | `list[str]` | 天干顺序 | | `earthly_branches` | `list[str]` | 地支顺序 | | `zodiac` | `list[str]` | 生肖标识,按地支顺序 | | `signs` | `list[str]` | 星座标识,按黄道顺序 | | `palaces` | `list[str]` | 十二宫名,从命宫起**逆时针**排 | | `gender` | `dict[str, str]` | 男女各自的阴阳 | | `chinese_time` | `list[str]` | 时辰标识,早子时起、晚子时止 | | `time_range` | `list[str]` | 时辰对应的钟点区间 | | `tiger_rule` | `dict[str, str]` | 五虎遁:年干推正月天干 | | `rat_rule` | `dict[str, str]` | 五鼠遁:日干推子时天干 | | `mutagen` | `list[str]` | 四化顺序 | | `five_elements_class` | `dict[str, int]` | 五行局标识 → 局数(水二局 2 …… 火六局 6) | **示例** ```python c = data.constants() print(c.languages) print(c.zodiac[:3], c.chinese_time[12], c.time_range[2]) print(c.gender) print("甲年正月干:", c.tiger_rule["jiaHeavenly"]) print(c.palaces) print(c.five_elements_class) ``` **输出** ```text ['en-US', 'ja-JP', 'ko-KR', 'zh-CN', 'zh-TW', 'vi-VN'] ['rat', 'ox', 'tiger'] lateRatHour 03:00~05:00 {'female': '阴', 'male': '阳'} 甲年正月干: bingHeavenly ['soulPalace', 'parentsPalace', 'spiritPalace', 'propertyPalace', 'careerPalace', 'friendsPalace', 'surfacePalace', 'healthPalace', 'wealthPalace', 'childrenPalace', 'spousePalace', 'siblingsPalace'] {'earth5th': 5, 'fire6th': 6, 'metal4th': 4, 'water2nd': 2, 'wood3rd': 3} ``` **边界与陷阱** `palaces` 给的是宫名的**排列顺序**:命、父母、福德、田宅、官禄、仆役、迁移、 疾厄、财帛、子女、夫妻、兄弟。这是十二宫从命宫起逆时针铺开的次序, 不是某张盘上第 `i` 格叫什么。要那个用 [`utils.get_palace_names(soul_index)`](/zh/docs/python/util#get_palace_names)。 `languages` 的顺序是 iztro 词表的合并次序(en-US 起),不是 `Language` 枚举的声明次序。 [`i18n.key_of`](/zh/docs/python/i18n#key_of) 的逐语言扫描顺序与它一致。 `five_elements_class`、`gender`、`tiger_rule`、`rat_rule` 四项是 `dict` 而非 `list`, 键的次序是字典序(原生扩展转过来时排过序),不代表五行局或天干的排列顺序。 局数也可以直接从枚举取:`FiveElementsClass.WOOD_3.number`。 *** ## 枚举清单 [#枚举清单] `x_iztro.enums` 里的每个枚举都是 `StrEnum`,取值就是语言无关标识。 从包根直接导入:`from x_iztro import MajorStar, PalaceName`。 | 枚举 | 成员数 | 成员 | | -------------------------------- | ------ | --------------------------------------------------------------------------------------------------------------------------------------------------------- | | `Gender` | 2 | `MALE` `FEMALE` | | `Language` | 6 | `ZH_CN` `ZH_TW` `EN_US` `JA_JP` `KO_KR` `VI_VN` | | `HeavenlyStem` | 10 | `JIA` `YI` `BING` `DING` `WU` `JI` `GENG` `XIN` `REN` `GUI` | | `EarthlyBranch` | 12 | `ZI` `CHOU` `YIN` `MAO` `CHEN` `SI` `WU` `WEI` `SHEN` `YOU` `XU` `HAI` | | `PalaceName` | **14** | 十二宫 `SOUL` `SIBLINGS` `SPOUSE` `CHILDREN` `WEALTH` `HEALTH` `SURFACE` `FRIENDS` `CAREER` `PROPERTY` `SPIRIT` `PARENTS`,外加两个定位标记 `BODY`(身宫)`ORIGINAL`(来因宫) | | `FiveElementsClass` | 5 | `WATER_2` `WOOD_3` `METAL_4` `EARTH_5` `FIRE_6` | | `Mutagen` | 4 | `LU` `QUAN` `KE` `JI` | | `Brightness` | 7 | `MIAO` `WANG` `DE` `LI` `PING` `BU` `XIAN` | | `StarType` | 8 | `MAJOR` `SOFT` `TOUGH` `ADJECTIVE` `FLOWER` `HELPER` `LUCUN` `TIANMA` | | `Scope` | 6 | `ORIGIN` `DECADAL` `YEARLY` `MONTHLY` `DAILY` `HOURLY` | | `MajorStar` | 14 | 十四主星,`ZIWEI` `TIANJI` `TAIYANG` `WUQU` `TIANTONG` `LIANZHEN` `TIANFU` `TAIYIN` `TANLANG` `JUMEN` `TIANXIANG` `TIANLIANG` `QISHA` `POJUN` | | `MinorStar` | 14 | 十四辅星,`ZUOFU` `YOUBI` `WENCHANG` `WENQU` `LUCUN` `TIANMA` `QINGYANG` `TUOLUO` `HUOXING` `LINGXING` `TIANKUI` `TIANYUE` `DIKONG` `DIJIE` | | `AdjectiveStar` | 43 | 杂耀,含中州派的 `XUNZHONG`(旬中);成员对照见[星耀](/zh/docs/guide/concepts/stars) | | `HoroscopeStar` | 50 | 五个运限层级各十颗流耀,`YUNLU` `LIULU` `YUELU` `RILU` `SHILU` 等 | | `Changsheng12` | 12 | `CHANGSHENG` `MUYU` `GUANDAI` `LINGUAN` `DIWANG` `SHUAI` `BING` `SI` `MU` `JUE` `TAI` `YANG` | | `Boshi12` | 12 | `BOSHI` `LISHI` `QINGLONG` `XIAOHAO` `JIANGJUN` `ZHOUSHU` `FEILIAN` `XISHEN` `BINGFU` `DAHAO` `FUBING` `GUANFU` | | `Suiqian12` | 13 | `SUIJIAN` `HUIQI` `SANGMEN` `GUANSUO` `GWANFU` `XIAOHAO` `DAHAO` `SUIPO` `LONGDE` `BAIHU` `TIANDE` `DIAOKE` `BINGFU`(`SUIPO` 为中州派的岁破) | | `Jiangqian12` | 12 | `JIANGXING` `PANAN` `SUIYI` `XISHEN` `HUAGAI` `JIESHA` `ZHAISHA` `TIANSHA` `ZHIBEI` `XIANCHI` `YUESHA` `WANGSHEN` | | `Algorithm` | 2 | `DEFAULT` `ZHONGZHOU` | | `AstroType` | 3 | `HEAVEN` `EARTH` `HUMAN` | | `YearDivide` / `HoroscopeDivide` | 各 2 | `NORMAL` `EXACT` | | `AgeDivide` | 2 | `NORMAL` `BIRTHDAY` | | `DayDivide` | 2 | `FORWARD` `CURRENT` | 成员名与它的取值不总是拼音对应——`Boshi12.FEILIAN` 的值是 `faylian`, `Jiangqian12.XISHEN` 的值是 `xiishen`,`Suiqian12.GWANFU` 的值是 `gwanfu`。 这些拼写沿用 iztro 的词表,**判断一律用枚举成员**,不要手写字符串。 全部标识与译名的对照见[标识总表](/zh/docs/guide/guides/keys)。 ### FiveElementsClass.number [#fiveelementsclassnumber] 五行局枚举多一个属性 `number`,给出局数——大限起运岁数与起紫微都用它: ```python from x_iztro import FiveElementsClass for c in FiveElementsClass: print(c, c.number) ``` **输出** ```text water2nd 2 wood3rd 3 metal4th 4 earth5th 5 fire6th 6 ``` **示例** ```python from x_iztro import MajorStar, PalaceName, Mutagen soul = chart.palace(PalaceName.SOUL) print(soul.major_stars[0].key == MajorStar.ZIWEI) print(MajorStar.ZIWEI, Mutagen.LU, PalaceName.WEALTH) ``` **输出** ```text True ziweiMaj sihuaLu wealthPalace ``` `chart.palace(PalaceName.SOUL)` 与 `chart.palace("soulPalace")` 完全等价。 枚举的价值在于 IDE 补全与拼写检查,而非类型约束。 名字来自外部输入时,用构造函数当校验器:`PalaceName("soulPalce")` 抛 `ValueError: 'soulPalce' is not a valid PalaceName`, 比让查询方法静默返回 `None` 早一步暴露问题。 # 翻译 (/zh/docs/python/i18n) 标识与译名的双向查找。 星盘上每个字段都同时给出译名与 `*_key` 标识,通常不必手工翻译。 这两个函数用于手上只有标识(或只有某种语言的译名)、需要换算的场合。 ```python from x_iztro import i18n ``` 支持六种语言:`zh-CN`、`zh-TW`、`en-US`、`ja-JP`、`ko-KR`、`vi-VN`。 *** ## translate [#translate] **用途** 把任意标识译成指定语言。 **签名** ```python def translate(key: str, language: LanguageType = "zh-CN") -> str | None ``` **参数** | 参数 | 类型 | 必填 | 默认 | 说明 | | ---------- | ----- | -- | --------- | ------ | | `key` | `str` | 是 | — | 语言无关标识 | | `language` | `str` | 否 | `"zh-CN"` | 目标语言 | 覆盖十二类共 260 个标识: | 类目 | 数量 | 例 | | ----------- | --- | --------------------------------------------------------- | | 星耀 | 162 | `ziweiMaj`、`changsheng`、`yunlu` | | 宫位(含身宫、来因宫) | 14 | `soulPalace`、`wealthPalace`、`bodyPalace`、`originalPalace` | | 天干 | 10 | `jiaHeavenly` | | 地支 | 12 | `ziEarthly` | | 亮度 | 7 | `miao`、`wang` | | 四化 | 4 | `sihuaLu` | | 五行局 | 5 | `water2nd` | | 性别 | 2 | `male`、`female` | | 生肖 | 12 | `rat`、`ox` | | 时辰 | 13 | `earlyRatHour` | | 星座 | 12 | `aries` | | 运限层级 | 7 | `decadal`、`turn` | **返回值** 译名;未知标识返回 `None`。 **示例** ```python print(i18n.translate("ziweiMaj", "en-US")) print(i18n.translate("soulPalace", "ja-JP")) print(i18n.translate("ziweiMaj", "vi-VN")) print(i18n.translate("bodyPalace")) print(i18n.translate("nosuch")) ``` **输出** ```text emperor 命宮 Tử Vi 身宫 None ``` *** ## key\_of [#key_of] **用途** 由任意语言的译名反查标识。 **签名** ```python def key_of(text: str, key_filter: str | None = None) -> str | None ``` **参数** | 参数 | 类型 | 必填 | 默认 | 说明 | | ------------ | ------------- | -- | ------ | ------------------- | | `text` | `str` | 是 | — | 任一支持语言下的译名 | | `key_filter` | `str \| None` | 否 | `None` | 限定标识名须含的子串,用于消歧同形译名 | **返回值** 标识;查不到返回 `None`。 **示例** ```python print(i18n.key_of("紫微")) print(i18n.key_of("emperor")) print(i18n.key_of("자미")) print(i18n.key_of("查无此名")) ``` **输出** ```text ziweiMaj ziweiMaj ziweiMaj None ``` 三种语言的译名都落到同一个标识。 **边界与陷阱** 少数译名在多个类目下同形:en-US 的 `horse` 既是生肖马也是天马, `dragon` 既是生肖龙也是青龙,ko-KR 的 `사` 既是地支巳也是长生12神的死。 不限定时逐语言、每种语言内逐标识取先命中者,顺序与 iztro 的 `kot` 完全一致 (有金标测试逐例守着)。要指定类目就传 `key_filter`——标识名含该子串才纳入比对: ```python print(i18n.key_of("horse")) # horse(生肖马) print(i18n.key_of("horse", "Min")) # tianmaMin(天马) print(i18n.key_of("유시")) # hourly(流时) print(i18n.key_of("유시", "Hour")) # roosterHour(酉时) print(i18n.key_of("horse", "Palace")) # None ``` 常用子串:`Maj` 十四主星、`Min` 辅星、`Heavenly` / `Earthly` 干支、 `Palace` 宫位、`Hour` 时辰。限定后无匹配返回 `None`,不退回未限定的结果。 `key_of` 会遍历 260 个标识 × 6 种语言。单次调用开销可忽略, 但不要放在每宫每星的内层循环里——那种场合直接用数据自带的 `*_key` 字段。 *** ## all\_keys [#all_keys] **用途** 取全部 260 个可翻译标识。 **签名** ```python def all_keys() -> list[str] ``` **返回值** 标识列表,顺序即 `key_of` 的反查次序: 运限层级、生肖、时辰、星座、五行局、天干、地支、亮度、四化、星耀、宫位、性别, 与 iztro 各语言翻译文件的合并次序一致。 **示例** ```python keys = i18n.all_keys() print(len(keys)) print(keys[:4]) print(i18n.translate(keys[0])) ``` **输出** ```text 260 ['decadal', 'childhood', 'yearly', 'monthly'] 大限 ``` 要遍历某一类目自己的标识时,用 `enums` 里的枚举或 `data.constants()` 更省事。 *** ## 没有全局语言开关 [#没有全局语言开关] x-iztro 不设「当前语言」这样的全局状态:排盘时语言随参数传入, 翻译函数每次调用都显式指定目标语言。 全局语言开关会让同一段代码在不同调用顺序下产出不同结果, 多线程环境尤其危险。显式传参使每次调用的结果只由入参决定。 要在一个进程里同时输出多种语言,直接排多张盘即可,互不干扰: ```python zh = Astro().by_solar("2000-8-16", 2, "female") en = Astro().by_solar("2000-8-16", 2, "female", language="en-US") print(zh.palace("soulPalace").major_stars[0].name, en.palace("soulPalace").major_stars[0].name) ``` **输出** ```text 紫微 emperor ``` 两张盘的 `*_key` 字段完全相同,因此任何基于标识的判断在两张盘上结果一致。 # 扩展星盘 (/zh/docs/python/extend) 用插件给 Astrolabe 类挂自定义分析方法。 斗数的分析规则千人千面,库不可能穷举。x-iztro 让你把自己的规则 以方法的形式挂到星盘类上——调用语法与内置方法一致,所有星盘实例都能用。 ## 配方 [#配方] 一个插件就是一个函数,接受 `Astrolabe` 类并往上挂方法。 写一个接受 `type[Astrolabe]` 的函数 在函数体里定义方法,赋值到类上 调 `load_plugin` 加载 ```python from x_iztro import Astro, Astrolabe, PalaceName from x_iztro.plugin import load_plugin def my_analysis(cls: type[Astrolabe]) -> None: """给星盘补两个自定义分析方法。""" def major_star(self) -> str: """命宫主星名(空宫借对宫),多颗以逗号分隔""" soul = self.palace(PalaceName.SOUL) source = soul.opposite_palace() if soul.is_empty() else soul return ",".join(s.name for s in source.major_stars) def five_elements_value(self) -> int: """五行局的局数""" return int(self.five_elements_class_key[-3]) cls.major_star = major_star cls.five_elements_value = five_elements_value load_plugin(my_analysis) ``` **用法** ```python chart = Astro().by_solar("2000-8-16", 2, "female") print(chart.major_star()) # 扩展方法随排盘语言输出 en = Astro().by_solar("2000-8-16", 2, "female", language="en-US") print(en.major_star()) ``` **输出** ```text 紫微 emperor ``` *** ## load\_plugin / load\_plugins [#load_plugin--load_plugins] **签名** ```python def load_plugin(plugin: Plugin) -> None def load_plugins(plugins: Iterable[Plugin]) -> None ``` `Plugin` 的类型是 `Callable[[type[Astrolabe]], None]`。 **参数** | 参数 | 类型 | 必填 | 默认 | 说明 | | --------- | ------------------ | -- | -- | ------------------- | | `plugin` | `Plugin` | 是 | — | 接受 `Astrolabe` 类的函数 | | `plugins` | `Iterable[Plugin]` | 是 | — | 按顺序加载的多个插件 | **返回值** `None`。方法直接挂到类上。 **边界与陷阱** 方法挂在**类**上而非实例上,因此加载时机不影响已有实例—— 先排的盘在插件加载后一样能调用新方法。 `load_plugin` 传入非可调用对象时抛 `TypeError`。 `load_plugins` 逐个加载,遇到第一个不可调用的即报错,后续插件不会加载。 插件改的是 `Astrolabe` 类本身,进程内所有星盘都受影响。 同名方法会被后加载的插件覆盖。 *** ## 为什么能挂上去 [#为什么能挂上去] `Astrolabe` 是 `slots=True` 的 frozen dataclass,实例上挂不了属性: ```python try: chart.foo = 1 except Exception as e: print(type(e).__name__, e) ``` **输出** ```text FrozenInstanceError cannot assign to field 'foo' ``` 但**类**上挂方法不受影响。这正是插件需要的粒度: 插件改的是「所有星盘都有这个方法」,不是「这一张盘多了个字段」。 *** ## 扩展别的类型 [#扩展别的类型] 同一套写法适用于宫位与星耀,直接给对应的类挂方法即可: ```python from x_iztro.models import Palace def palace_analysis(cls: type[Palace]) -> None: def is_afflicted(self) -> bool: """本宫是否「煞忌交冲」:坐煞星且带化忌""" sha = ["qingyangMin", "tuoluoMin", "huoxingMin", "lingxingMin", "dikongMin", "dijieMin"] return self.has_one_of(sha) and self.has_mutagen("sihuaJi") cls.is_afflicted = is_afflicted palace_analysis(Palace) ``` ```python for p in chart.palaces: if p.is_afflicted(): print(p.name, "煞忌交冲") ``` **输出** ```text 疾厄 煞忌交冲 ``` `load_plugin` 只接受作用于 `Astrolabe` 的插件。要挂到别的类上, 像上例那样直接调用函数即可——插件机制本身没有魔法。 *** ## 组织建议 [#组织建议] `wealth_analysis`、`career_analysis`、`health_analysis` 各自成插件, 按需 `load_plugin`。堆成一个大插件会让所有使用方都被迫加载全部方法。 `s.key == MajorStar.ZIWEI` 在任何输出语言下都成立; `s.name == "紫微"` 只在中文盘上成立。展示时才用 `name`。 挂上去的方法对静态类型检查器不可见,调用处会被标为「未知属性」。 需要类型友好时,为扩展后的星盘声明一个 Protocol 或直接用 `cast`。 # 错误处理 (/zh/docs/python/errors) IztroError 的 code 分类、触发条件、消息格式与处理模式。 所有收外部输入的入口在入参非法时抛 `IztroError`。日期格式、日期存在性、 年份范围、时辰索引在核心层前置校验;性别、语言、标识、配置这些字符串取值在绑定层校验。 ```python from x_iztro import IztroError ``` `IztroError` 继承自 `ValueError`,因此既有的 `except ValueError` 照样接得住, 不必为升级改代码。 ## code [#code] 异常消息面向人,会随版本调整措辞;要在程序里分支请用 `.code`: | `code` | 含义 | 典型触发 | | -------------------- | --------- | ---------------------- | | `invalid_date` | 日期非法 | 格式错、日期不存在、超出 1583–9999 | | `invalid_time_index` | 时辰索引越界 | 不在 0–12 | | `invalid_argument` | 其余入参或配置非法 | 性别、语言、星耀标识、宫名、自定义表 | | `internal` | 库内部缺陷 | 应视为 bug 上报 | 这四个取值与 Rust 的 `IztroError::code()`、Go 的 `iztro.Error.Code` 是同一套, 跨语言分支逻辑可以照抄。 ```python from x_iztro import Astro, IztroError for args in [("2000-2-30", 2, "female"), ("2000-8-16", 13, "female"), ("2000-8-16", 2, "x")]: try: Astro().by_solar(*args) except IztroError as e: print(e.code, "|", e) ``` **输出** ```text invalid_date | invalid solar date '2000-2-30': day is out of range for that month invalid_time_index | time_index must be 0-12, got 13 invalid_argument | invalid gender 'x': expected 'male' or 'female' ``` 库内部缺陷导致的 panic 在绑定层被兜住,转成 `code` 为 `internal` 的 `IztroError`, 不会漏出 `except Exception` 捕获不到的 `pyo3_runtime.PanicException`。 *** ## 日期相关 [#日期相关] | 情形 | 例 | 消息 | | --------------- | ------------- | ------------------------------------ | | 格式不是 `YYYY-M-D` | `"2000/8/16"` | `expected 'YYYY-M-D'` | | 年、月、日不是数字 | `"abc-8-16"` | `year is not a number` | | 月份越界 | `"2000-13-1"` | `month must be within 1-12` | | 该月没有这一天 | `"2000-2-30"` | `day is out of range for that month` | | 年份超出支持范围 | `"1500-1-1"` | `year must be within 1583-9999` | 农历专有(只有 `by_lunar` 与两个 `*_by_lunar_date` 查询会碰到): | 情形 | 例 | 消息 | | --------- | -------------------- | ------------------------------------------ | | 该农历年没有这个月 | 表内缺月 | `month does not exist in that lunar year` | | 该农历月没有这一天 | `"2000-7-30"`(七月是小月) | `day is out of range for that lunar month` | 农历消息的前缀是 `invalid lunar date '<原串>': `,公历是 `invalid solar date '<原串>': `, 从消息就能看出走的是哪个入口。 **示例** ```python from x_iztro import Astro for date in ["2000-13-1", "2000-2-30", "1500-1-1"]: try: Astro().by_solar(date, 2, "female") except IztroError as e: print(e) ``` **输出** ```text invalid solar date '2000-13-1': month must be within 1-12 invalid solar date '2000-2-30': day is out of range for that month invalid solar date '1500-1-1': year must be within 1583-9999 ``` 消息里带上了原始输入,便于在批量处理时定位是哪一条数据出的问题。 **边界与陷阱** 1582 年格里历改革当年有一段不存在的日期。底层历法库在这些日期上没有定义, 因此支持范围从改革完成后的 1583 年起算。上限 9999 是农历数据表的覆盖终点。 `"2000-8-16"` 与 `"2000-08-16"` 都接受。分隔符必须是 `-`。 `by_lunar` 会检查该农历年该月是否真的存在,以及该月有多少天(大月 30、小月 29)。 `is_leap_month` 传 `True` 但那年那月无闰月时不报错,参数被静默忽略。 *** ## 时辰索引 [#时辰索引] **触发条件** 时辰索引不在 0–12。 **示例** ```python try: Astro().by_solar("2000-8-16", 13, "female") except IztroError as e: print(e) ``` **输出** ```text time_index must be 0-12, got 13 ``` **边界与陷阱** 子时跨午夜,拆成早子时(索引 0)与晚子时(索引 12),因此合法值有 13 个。 从小时数换算用 [`utils.time_to_index`](/zh/docs/python/util#time_to_index), 它保证结果落在合法范围。 *** ## 性别与语言 [#性别与语言] `code` 均为 `invalid_argument`。 | 参数 | 合法值 | 消息 | | ---------- | --------------------- | --------------------------------------------------------------------------------- | | `gender` | `"male"` / `"female"` | `invalid gender 'x': expected 'male' or 'female'` | | `language` | 六个语言代码 | `invalid language 'xx': expected one of zh-CN, zh-TW, en-US, ja-JP, ko-KR, vi-VN` | 语言代码大小写不敏感,`"zh-cn"` 与 `"zh-CN"` 都接受。 用 `Gender` / `Language` 枚举传参可以在写错的当场被 IDE 挡下。 *** ## 标识相关 [#标识相关] 工具函数与安星函数收的是语言无关标识,未知标识会报错: ```python from x_iztro import utils try: utils.get_brightness("nosuchstar", 0) except IztroError as e: print(e.code, "|", e) ``` **输出** ```text invalid_argument | unknown star key 'nosuchstar' ``` 这些函数只认标识不认译名。传 `"紫微"` 会得到 `unknown star key '紫微'`——先用 [`i18n.key_of`](/zh/docs/python/i18n#key_of) 换算。 星盘上的**查询方法**(`chart.palace`、`chart.star`、`palace.has`)是另一套规则: 它们同时接受标识与当前语言的译名,且查不到时静默返回 `None` / `False` 而不报错。 详见[星盘对象](/zh/docs/python/astrolabe#palace)。 *** ## 配置相关 [#配置相关] `ChartConfig` 的六个开关与两张自定义表都在排盘时校验,`code` 均为 `invalid_argument`: | 情形 | 消息 | | ------------ | -------------------------------------------------------------------------------- | | 开关取值未知 | `invalid yearDivide 'nope': expected 'normal' or 'exact'` | | 四化表的天干标识未知 | `invalid mutagens key 'nope': unknown heavenly stem` | | 某个天干的四化不是四项 | `invalid mutagens for 'jiaHeavenly': expected 4 stars (lu, quan, ke, ji), got 1` | | 某颗星的亮度表不是十二项 | `invalid brightness for 'ziweiMaj': expected 12 entries, got 1` | 长度是**严格**校验:四化必须正好四项、亮度必须正好十二项,多一项少一项都报错。 自定义表只收标识不收译名。 *** ## 处理模式 [#处理模式] **批量处理时跳过坏数据** ```python rows = [ {"date": "2000-8-16", "ti": 2, "gender": "female"}, {"date": "2000-2-30", "ti": 2, "gender": "female"}, {"date": "1990-3-3", "ti": 13, "gender": "male"}, ] charts, failed = [], [] astro = Astro() for row in rows: try: charts.append(astro.by_solar(row["date"], row["ti"], row["gender"])) except IztroError as e: failed.append((row["date"], e.code)) print(len(charts), failed) ``` **输出** ```text 1 [('2000-2-30', 'invalid_date'), ('1990-3-3', 'invalid_time_index')] ``` **转成自己的异常类型** ```python class ChartError(Exception): def __init__(self, code: str, message: str): super().__init__(message) self.code = code def build(date: str, ti: int, gender: str): try: return Astro().by_solar(date, ti, gender) except IztroError as e: raise ChartError(e.code, f"排盘失败: {e}") from e try: build("2000-2-30", 2, "female") except ChartError as e: print(e.code, "|", e) ``` **输出** ```text invalid_date | 排盘失败: invalid solar date '2000-2-30': day is out of range for that month ``` `code` 转出去之后,上层就不必再解析文案。 # 概览 (/zh/docs/go) 包结构、类型体系与阅读本参考的方式。 Go 包内嵌一份 WebAssembly 编译的核心,经纯 Go 的 wazero 运行时调用—— **不需要 cgo**,交叉编译与静态链接都不受影响。 这一栏是 Go 侧的完整 API 参考——每个导出函数、类型与方法都有独立条目。 ## 安装 [#安装] ```bash go get github.com/x-haose/x-iztro/go/iztro ``` ```go import "github.com/x-haose/x-iztro/go/iztro" ``` ## 第一张盘 [#第一张盘] ```go chart, err := iztro.BySolar("2000-8-16", 2, iztro.GenderFemale, true, iztro.LanguageZhCN, nil) if err != nil { log.Fatal(err) } fmt.Println(chart.SolarDate, chart.LunarDate) // 2000-8-16 二〇〇〇年七月十七 soul := chart.Palace(iztro.PalaceSoul) if len(soul.MajorStars) > 0 { fmt.Println(soul.MajorStars[0].Name) // 紫微 } else { fmt.Println("命宫是空宫,借对宫看:", soul.OppositePalace().MajorStars[0].Name) } ``` 一张盘上通常有两宫无主星(空宫),命宫也可能是其中之一。 直接写 `soul.MajorStars[0]` 在那种盘上会 panic——先判长度,或用 [`IsEmpty`](/zh/docs/go/palace#isempty) 分支到借对宫的写法。 ## 包结构 [#包结构] 包是扁平的,全部导出项都在 `iztro` 下。按主题划分: | 主题 | 主要导出 | 本参考对应页 | | ---- | ------------------------------------------------ | ---------------------------------- | | 排盘 | `BySolar`、`ByLunar`、`Rearranged` | [排盘入口](/zh/docs/go/astro) | | 数据类型 | `Astrolabe`、`Palace`、`Star`、`Horoscope`、`Config` | [星盘对象](/zh/docs/go/astrolabe) 起的四页 | | 标识常量 | `PalaceSoul`、`StarZiweiMaj`、`MutagenLu` 等 | [数据表](/zh/docs/go/data) | | 轻量查询 | `GetZodiacBySolarDate` 等 | [轻量查询](/zh/docs/go/query) | | 工具函数 | `FixIndex`、`GetBrightness` 等 | [工具函数](/zh/docs/go/util) | | 安星 | `GetMajorStar`、`GetHoroscopeStar` 等 | [安星模块](/zh/docs/go/star) | | 数据表 | `StarsInfo`、`HeavenlyStems` 等 | [数据表](/zh/docs/go/data) | | 翻译 | `Translate`、`KeyOf`、`KeyOfIn` | [翻译](/zh/docs/go/i18n) | | 错误 | `*Error`、`Err*` 哨兵、`Code*` 常量 | [错误处理](/zh/docs/go/errors) | | 运行时 | `Warmup`、`Close`、`CompilationCacheDir` | 本页下方 | ## 常量即标识 [#常量即标识] 包里的标识常量取值就是语言无关标识,可直接与数据对象的 `*Key` 字段比较: ```go soul := chart.Palace(iztro.PalaceSoul) fmt.Println(soul.MajorStars[0].Key == iztro.StarZiweiMaj) // true ``` 它们都是无类型字符串常量,字符串字面量同样有效—— `chart.Palace("soulPalace")` 与 `chart.Palace(iztro.PalaceSoul)` 等价。 常量的价值在于 IDE 补全与拼写检查。 `star.Name` 随排盘语言变化(中文盘是「紫微」,英文盘是 `emperor`); `star.Key` 在任何语言下都是 `ziweiMaj`。所有判断都应基于 `*Key` 字段或内置判断方法。 ## 可变参数 [#可变参数] 需要传星耀列表或四化列表的方法一律用可变参数,调用处不必构造切片: ```go soul := chart.Palace(iztro.PalaceSoul) target := chart.Palace(iztro.PalaceWealth) fmt.Println(soul.Has(iztro.StarZiweiMaj, iztro.StarTianxiangMaj)) fmt.Println(soul.FliesTo(target, iztro.MutagenLu, iztro.MutagenJi)) ``` **输出** ```text false false ``` 已有切片时用 `...` 展开: ```go soul := chart.Palace(iztro.PalaceSoul) stars := []string{iztro.StarZiweiMaj, iztro.StarTianxiangMaj} fmt.Println(soul.Has(stars...)) ``` **输出** ```text false ``` `HasHoroscopeStars` 一族的星耀参数是 `[]string` 而非可变参数, 因为它前面已有宫名与层级两个字符串参数,可变参数会让调用处产生歧义。 ## 错误处理 [#错误处理] 需要计算的入口都返回 `(值, error)`;纯查询方法(`Palace`、`Star`、`Has` 等)不返回错误, 查不到时返回 `nil` 或零值。 ```go _, err := iztro.BySolar("2000-13-1", 2, iztro.GenderFemale, true, iztro.LanguageZhCN, nil) fmt.Println(err) fmt.Println(errors.Is(err, iztro.ErrInvalidDate)) var e *iztro.Error if errors.As(err, &e) { fmt.Println(e.Code) } ``` **输出** ```text iztro: invalid solar date '2000-13-1': month must be within 1-12 true invalid_date ``` 错误一律是 `*iztro.Error`,带机器可读的 `Code`,并可用 `errors.Is` 对四个哨兵匹配。 详见[错误处理](/zh/docs/go/errors)。 ## 运行时与性能 [#运行时与性能] wasm 模块**编译一次、实例按需铺开**:实例各持有自己的线性内存, 因此并发调用各占一个实例而不是共享一个加锁,实例数上限为 `GOMAXPROCS`。 排盘的热路径上没有全局互斥锁——取实例走 channel,只有运行时首次初始化与 `Close` 才加锁。多 goroutine 同时排盘因此能真正并行:同一台十核机器上, 8 个 goroutine 跑 800 次排盘比单 goroutine 快四倍出头。 编译产物落盘缓存在用户缓存目录下(`CompilationCacheDir` 可查), 按 wasm 内容哈希分桶,换了 wasm 自然换桶。 实测量级(Apple M 系列,10 核;具体数字随机器与 wasm 体积浮动): | 阶段 | 耗时 | | --------------------------- | ----- | | 首次调用,编译缓存**未命中**(要现编译 wasm) | 一两百毫秒 | | 首次调用,编译缓存命中 | 二三十毫秒 | | 稳态单次排盘 | 半毫秒上下 | 稳态那半毫秒里,大头是 wasm 侧把整张盘序列化成 JSON、Go 侧再反序列化成结构体, 而不是斗数推算本身。只需要个别字段时,用 [轻量查询](/zh/docs/go/query)(`GetMajorStarBySolarDate` 一类)比排整盘划算得多。 ### Warmup / Close / CompilationCacheDir [#warmup--close--compilationcachedir] ```go func Warmup(ctx context.Context) error func Close(ctx context.Context) error func CompilationCacheDir(ctx context.Context) (string, error) ``` | 函数 | 说明 | | --------------------- | ---------------------------------------------------- | | `Warmup` | 预先完成编译并把实例池铺满,把冷启动开销提前到启动阶段。不调用也能正常工作——编译与实例化本来就是惰性的 | | `Close` | 关闭运行时、归还全部实例内存。通常不必调用;关闭后再调用本包任何函数会自动重新初始化 | | `CompilationCacheDir` | 返回编译缓存目录;未启用落盘缓存时返回空串 | ```go ctx := context.Background() if err := iztro.Warmup(ctx); err != nil { log.Fatal(err) } dir, err := iztro.CompilationCacheDir(ctx) if err != nil { log.Fatal(err) } fmt.Println(dir != "") chart, err := iztro.BySolar("2000-8-16", 2, iztro.GenderFemale, true, iztro.LanguageZhCN, nil) if err != nil { log.Fatal(err) } fmt.Println(chart.SolarDate) ``` **输出** ```text true 2000-8-16 ``` 服务进程希望第一个请求就走热路径时,在启动阶段调一次 `Warmup` 即可。 ### Context 变体 [#context-变体] 排盘、运限、重排、Prompt 这些要进 wasm 的入口都有一个 `*Context` 版本, 多收一个 `context.Context`: | 无 ctx | 带 ctx | | --------------------------------------------------- | ------------------------------------------------------- | | `BySolar` / `ByLunar` | `BySolarContext` / `ByLunarContext` | | `Astrolabe.Horoscope` / `HoroscopeNow` | `HoroscopeContext` / `HoroscopeNowContext` | | `Astrolabe.Rearranged` | `RearrangedContext` | | `Astrolabe.AstrolabeToPrompt` / `HoroscopeToPrompt` | `AstrolabeToPromptContext` / `HoroscopeToPromptContext` | | — | `Warmup` / `Close` / `CompilationCacheDir` 只有 ctx 版本 | ```go ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second) defer cancel() chart, err := iztro.BySolarContext(ctx, "2000-8-16", 2, iztro.GenderFemale, true, iztro.LanguageZhCN, nil) if err != nil { log.Fatal(err) } fmt.Println(chart.SolarDate) ``` **输出** ```text 2000-8-16 ``` `ctx` 用于取消**等待空闲 wasm 实例**的排队。实例一旦拿到,wasm 侧的计算不可中断—— 单次排盘本来就是亚毫秒级,没有需要中途打断的长任务。 这两个架构上 wazero 走优化编译器(编译成机器码)。其余架构回落到解释器, 仍然能跑出正确结果,但速度会低一个量级以上。生产环境请部署在 amd64 或 arm64 上。 ## 条目怎么读 [#条目怎么读] 每个 API 条目按固定八段组织: **用途** —— 一句话说清它做什么 **斗数含义** —— 它在紫微斗数里对应什么概念(纯工程性的函数省略此段) **签名** —— 从源码原样摘出 **参数** —— 名、类型、是否必填、默认值、说明 **返回值** —— 类型与结构 **示例** —— 可直接运行的片段 **输出** —— 该示例的真实运行结果 **边界与陷阱** —— 空值、越界、配置影响、与其他 API 的相互作用 示例统一用同一张盘:**2000 年 8 月 16 日寅时女命**, 方便跨页对照。这张盘的完整数据见[数据结构](/zh/docs/guide/data-model)。 # 排盘入口 (/zh/docs/go/astro) BySolar、ByLunar、Rearranged 与 AI Prompt 生成。 排盘是一切的起点:给出生日期、时辰、性别,得到一个 `*Astrolabe`。 所有入口都返回 `error`。日期格式与存在性、公历年份范围、时辰索引、性别、 语言、配置都在核心层前置校验。详见[错误处理](/zh/docs/go/errors)。 *** ## BySolar [#bysolar] **用途** 由公历日期排出本命盘。 **斗数含义** 紫微斗数以农历为算法基础,但绝大多数人只记得公历生日。 本函数先把公历转农历(含年、月、日、时四柱),再据此安星。 换年的时点受 `YearDivide` 影响——正月初一与立春之间出生的人, 两种配置会得到不同的年干支,进而影响四化、命主身主与全部年系星。 **签名** ```go func BySolar( solarDate string, timeIndex uint8, gender Gender, fixLeap bool, language Language, config *Config, ) (*Astrolabe, error) ``` **参数** | 参数 | 类型 | 必填 | 默认 | 说明 | | ----------- | ---------- | -- | -- | ------------------------------------------------------------------------------ | | `solarDate` | `string` | 是 | — | 公历日期,格式 `YYYY-M-D`,月日不必补零。支持 1583–9999 年 | | `timeIndex` | `uint8` | 是 | — | 时辰索引 0–12。0 为早子时(00:00–01:00),12 为晚子时(23:00–24:00) | | `gender` | `Gender` | 是 | — | `GenderMale` 或 `GenderFemale`(字面量 `"male"`/`"female"` 也可)。决定大限顺逆与长生、博士十二神的排列方向 | | `fixLeap` | `bool` | 是 | — | 是否调整农历闰月。为真时闰月十六日起按下月算(晚子时除外,见下) | | `language` | `Language` | 是 | — | 盘面语言(`LanguageZhCN` 等),影响所有译名字段;`*Key` 标识字段不受影响 | | `config` | `*Config` | 是 | — | 排盘配置,传 `nil` 取默认 | **返回值** `*Astrolabe`——十二宫、四柱、命主身主、五行局俱全的完整星盘。 **示例** ```go chart, err := iztro.BySolar("2000-8-16", 2, iztro.GenderFemale, true, iztro.LanguageZhCN, nil) if err != nil { log.Fatal(err) } fmt.Println(chart.SolarDate, "|", chart.LunarDate, "|", chart.ChineseDate) fmt.Println(chart.Sign, chart.Zodiac, chart.FiveElementsClass) fmt.Println("命主", chart.Soul, "身主", chart.Body) ``` **输出** ```text 2000-8-16 | 二〇〇〇年七月十七 | 庚辰 甲申 丙午 庚寅 狮子座 龙 木三局 命主 破军 身主 文昌 ``` **边界与陷阱** 子时横跨午夜,分早子时(00:00–01:00,属当日)与晚子时(23:00–24:00,属次日)。 两者的日柱不同,紫微起宫也可能差一天,因此必须区分,索引才有 13 个。 不确定时辰索引时用 `TimeToIndex(hour)` 换算。 进位要同时满足四个条件:该农历月确实是闰月、`fixLeap` 为 `true`、 农历日大于 15、且时辰索引不是 12(晚子时)。四者缺一,月索引就按本月算。 因此只有农历闰月下半月出生的人,`true` 与 `false` 会得到不同的月索引, 进而影响左辅右弼与全部月系星。 `&Config{}` 与 `nil` 效果相同——所有字段都有 `omitempty`,空值不会覆盖默认。 但显式写 `nil` 更清楚表达「用默认配置」。 *** ## ByLunar [#bylunar] **用途** 由农历日期排出本命盘。 **斗数含义** 农历日期是斗数的原生输入,跳过公历转换这一步。 知道自己农历生日的人直接用它,结果与用对应公历日期调 `BySolar` 完全一致。 **签名** ```go func ByLunar( lunarDate string, timeIndex uint8, gender Gender, leap LeapMonth, language Language, config *Config, ) (*Astrolabe, error) ``` **参数** 除以下两项外,其余与 `BySolar` 相同;`BySolar` 的 `fixLeap` 在这里并入 `leap`。 | 参数 | 类型 | 必填 | 默认 | 说明 | | ----------- | ----------- | -- | -- | ------------------------------------------------------------------------------------------------------------------------------------------- | | `lunarDate` | `string` | 是 | — | 农历日期,格式 `YYYY-M-D`,月份写正数(闰月由下一参数标记) | | `leap` | `LeapMonth` | 是 | — | `NotLeapMonth` 非闰月;`LeapMonthKeep` 闰月、按闰月本身排;`LeapMonthFixed` 闰月且十五之后视作次月(iztro `fixLeap`)。标为闰月但那年那月没有闰月时按普通月处理;其它取值返回 `ErrInvalidArgument` | **返回值** 同 `BySolar`。 **示例** ```go a, _ := iztro.ByLunar("2000-7-17", 2, iztro.GenderFemale, iztro.NotLeapMonth, iztro.LanguageZhCN, nil) b, _ := iztro.BySolar("2000-8-16", 2, iztro.GenderFemale, true, iztro.LanguageZhCN, nil) fmt.Println(a.SolarDate, a.SolarDate == b.SolarDate) ``` **输出** ```text 2000-8-16 true ``` **边界与陷阱** `leap` 标为闰月但那个月并非闰月时,按普通月排盘,不返回错误(与 iztro 一致)。 如果需要严格校验,调用前先自行确认该年该月确实有闰月。 *** ## Config [#config] 排盘配置。所有字段都可省略,省略即取默认。 ```go type Config struct { YearDivide string HoroscopeDivide string AgeDivide string DayDivide string Algorithm string AstroType string Mutagens map[string][]string Brightness map[string][]string } ``` | 字段 | 取值 | 默认 | 说明 | | ----------------- | ---------------------------------- | ----------- | -------------- | | `YearDivide` | `"normal"` / `"exact"` | `"normal"` | 年干支按正月初一还是立春换年 | | `HoroscopeDivide` | `"normal"` / `"exact"` | `"normal"` | 流年神煞按哪个分界取年支 | | `AgeDivide` | `"normal"` / `"birthday"` | `"normal"` | 虚岁按农历年还是生日增长 | | `DayDivide` | `"forward"` / `"current"` | `"forward"` | 晚子时归次日还是当日 | | `Algorithm` | `"default"` / `"zhongzhou"` | `"default"` | 算法派别 | | `AstroType` | `"heaven"` / `"earth"` / `"human"` | `"heaven"` | 排盘视角 | | `Mutagens` | 天干标识 → 四星标识 | — | 自定义四化表,按天干整表替换 | | `Brightness` | 星耀标识 → 十二项亮度标识 | — | 自定义亮度表,按星耀整表替换 | **示例** ```go cfg := &iztro.Config{ Algorithm: iztro.AlgorithmZhongzhou, YearDivide: iztro.YearDivideExact, } chart, _ := iztro.BySolar("2000-8-16", 2, iztro.GenderFemale, true, iztro.LanguageZhCN, cfg) fmt.Println(chart.FiveElementsClass) ``` **输出** ```text 木三局 ``` 每组取值都有对应的常量,不必手写字符串: `YearDivideNormal` / `YearDivideExact`、`HoroscopeDivideNormal` / `HoroscopeDivideExact`、 `AgeDivideNormal` / `AgeDivideBirthday`、`DayDivideForward` / `DayDivideCurrent`、 `AlgorithmDefault` / `AlgorithmZhongzhou`、`AstroHeaven` / `AstroEarth` / `AstroHuman`。 **边界与陷阱** `Mutagens["jiaHeavenly"]` 必须给满四项(禄权科忌), `Brightness["ziweiMaj"]` 必须给满十二项,多一项少一项都排盘报错 (`*Error`,`Code` 为 `invalid_argument`)。未列出的天干与星耀仍用默认表。 两张表的键与值都只收标识,不收译名。 `chart.Config` 由输出 DTO 还原,只含六个开关——两张自定义表是排盘**输入**而非结果, 不进 DTO(这一点与 JS iztro 的字段契约一致)。 但星盘内部保留了你传进来的原件,因此 `Rearranged`、`Horoscope`、Prompt 这些二次计算仍然用得上那两张表,不会静默丢失。 要把配置记录下来,请在自己的调用侧保存 `*Config`。 ```go cfg := &iztro.Config{ AstroType: iztro.AstroEarth, Mutagens: map[string][]string{ iztro.StemGeng: {iztro.StarTaiyangMaj, iztro.StarWuquMaj, iztro.StarTianfuMaj, iztro.StarTiantongMaj}, }, } chart, err := iztro.BySolar("2000-8-16", 2, iztro.GenderFemale, true, iztro.LanguageZhCN, cfg) if err != nil { log.Fatal(err) } fmt.Println(chart.FiveElementsClass, chart.Config.AstroType) fmt.Println(chart.Config.Mutagens == nil) fmt.Println(chart.Palace(iztro.PalaceSoul).MutagenStarKeys) ``` **输出** ```text 土五局 earth true [tiantongMaj tianjiMaj wenchangMin lianzhenMaj] ``` 所有字段都带 `omitempty`,空值不进 JSON,因此不会覆盖默认。 只想改一个开关时,构造一个只填那一项的 `&Config{...}` 即可。 *** ## Rearranged [#rearranged] **用途** 以指定干支为命宫重排本盘,返回新盘;原盘不变。 **斗数含义** 中州派把同一组出生数据看作三张盘:天盘以命宫干支起五行局, 地盘以身宫干支起,人盘以福德宫干支起。起局的干支一变,五行局就变, 紫微天府落点、十二宫名、长生十二神、大限小限随之全部重算。 本方法把这个能力放开到**任意干支**。 **签名** ```go func (a *Astrolabe) Rearranged(fromStemKey string, fromBranchKey string) (*Astrolabe, error) ``` **参数** | 参数 | 类型 | 必填 | 默认 | 说明 | | --------------- | -------- | -- | -- | -------- | | `fromStemKey` | `string` | 是 | — | 新命宫的天干标识 | | `fromBranchKey` | `string` | 是 | — | 新命宫的地支标识 | **返回值** 新的 `*Astrolabe`。重算:命宫身宫、五行局、十四主星、十二宫名、 长生十二神、大限小限、命主星,以及随命宫挪位的天伤、天使、天才。 沿用原盘:辅星、其余杂耀、博士十二神、岁前与将前十二神、身主星。 **示例** ```go chart, _ := iztro.BySolar("2000-8-16", 2, iztro.GenderFemale, true, iztro.LanguageZhCN, nil) // 从原盘身宫的干支起盘,等价于地盘 var body *iztro.Palace for i := range chart.Palaces { if chart.Palaces[i].IsBodyPalace { body = &chart.Palaces[i] } } earth, _ := chart.Rearranged(body.HeavenlyStemKey, body.EarthlyBranchKey) fmt.Println("天盘", chart.FiveElementsClass, "→ 地盘", earth.FiveElementsClass) ``` **输出** ```text 天盘 木三局 → 地盘 土五局 ``` **边界与陷阱** 天盘、地盘、人盘用 `&Config{AstroType: iztro.AstroEarth}` 直接排即可, 两个排盘入口都支持。`Rearranged` 是为「从任意干支起盘」准备的。 身主星按**出生年支**查表,与命宫位置无关,重排不改变出生年。 命主星按命宫地支查表,因此会跟着更新。 *** ## AstrolabeToPrompt / HoroscopeToPrompt [#astrolabetoprompt--horoscopetoprompt] **用途** 把星盘或运限渲染成结构化文本,供大模型消费。 **签名** ```go func (a *Astrolabe) AstrolabeToPrompt() (string, error) func (a *Astrolabe) HoroscopeToPrompt(targetDate string, targetTimeIndex uint8) (string, error) ``` **参数** | 参数 | 类型 | 必填 | 默认 | 说明 | | ----------------- | -------- | -- | -- | ------------ | | `targetDate` | `string` | 是 | — | 仅运限版本:目标公历日期 | | `targetTimeIndex` | `uint8` | 是 | — | 仅运限版本:目标时辰索引 | **返回值** `string`——按星盘的排盘语言输出的结构化文本。 **示例** ```go chart, _ := iztro.BySolar("2000-8-16", 2, iztro.GenderFemale, true, iztro.LanguageZhCN, nil) prompt, _ := chart.AstrolabeToPrompt() fmt.Println(string([]rune(prompt)[:36])) ``` **输出** ```text === 基本信息 === 性别: 女 阳历: 2000-8-16 农历: ``` **边界与陷阱** 输出语言跟随星盘的排盘语言,不单独设置。要英文 prompt 就用英文排盘。 # 星盘对象 (/zh/docs/go/astrolabe) Astrolabe 的字段、定位方法与三方四正判断。 `Astrolabe` 是排盘的产物,也是一切查询的入口。它持有十二宫的全部数据, 以及四柱、命主身主、五行局这些盘级信息。 ```go chart, _ := iztro.BySolar("2000-8-16", 2, iztro.GenderFemale, true, iztro.LanguageZhCN, nil) ``` 本页示例统一用 `"zh-CN"` 排盘,因此输出里的展示值都是中文。 换语言只改这些展示串,`*Key` 标识与所有判断方法的结果不变。 ## 字段 [#字段] | 字段 | 类型 | 说明 | | --------------------------- | -------- | ---------- | | `Gender` | `string` | 性别译名 | | `SolarDate` | `string` | 公历日期,与入参一致 | | `LunarDate` | `string` | 农历日期的中文写法 | | `ChineseDate` | `string` | 四柱展示串 | | `Time` | `string` | 时辰名 | | `TimeRange` | `string` | 时辰对应的钟点区间 | | `Sign` | `string` | 星座 | | `Zodiac` | `string` | 生肖 | | `Soul` | `string` | 命主星译名 | | `Body` | `string` | 身主星译名 | | `FiveElementsClass` | `string` | 五行局译名 | | `EarthlyBranchOfSoulPalace` | `string` | 命宫地支译名 | | `EarthlyBranchOfBodyPalace` | `string` | 身宫地支译名 | 展示字段随排盘语言翻译。要做判断请用下一组的 `*Key` 字段。 | 字段 | 类型 | 说明 | | ------------------------------ | -------- | ----------------------------- | | `GenderKey` | `Gender` | `GenderMale` / `GenderFemale` | | `SoulKey` | `string` | 命主星标识 | | `BodyKey` | `string` | 身主星标识 | | `FiveElementsClassKey` | `string` | 五行局标识 | | `EarthlyBranchOfSoulPalaceKey` | `string` | 命宫地支标识 | | `EarthlyBranchOfBodyPalaceKey` | `string` | 身宫地支标识 | 取值与包里的标识常量一一对应,可直接用 `==` 比较。 | 字段 | 类型 | 说明 | | ---------- | ---------- | ------------------- | | `Palaces` | `[]Palace` | 十二宫,索引 0 为寅宫、11 为丑宫 | | `RawDates` | `RawDates` | 结构化的农历生日与四柱干支标识 | `Palaces` 的索引是**宫位索引**而非宫名顺序:`Palaces[0]` 永远是寅宫, 命宫可能落在其中任何一格。取命宫用 `chart.Palace(iztro.PalaceSoul)`。 `RawDates` 是 `LunarDate` / `ChineseDate` 两个展示串的数据形式, 要做日期运算或按干支查表时用它,不必解析中文串: ```go type RawDates struct { LunarDate RawLunarDate `json:"lunarDate"` ChineseDate RawChineseDate `json:"chineseDate"` } type RawLunarDate struct { LunarYear int `json:"lunarYear"` // 农历年 LunarMonth int `json:"lunarMonth"` // 农历月 1–12,是否闰月看 IsLeap LunarDay int `json:"lunarDay"` // 农历日 1–30 IsLeap bool `json:"isLeap"` // 是否闰月 } type RawChineseDate struct { Yearly [2]string `json:"yearly"` // 年柱译名 [天干, 地支] YearlyKeys [2]string `json:"yearlyKeys"` // 年柱标识 Monthly [2]string `json:"monthly"` MonthlyKeys [2]string `json:"monthlyKeys"` Daily [2]string `json:"daily"` DailyKeys [2]string `json:"dailyKeys"` Hourly [2]string `json:"hourly"` HourlyKeys [2]string `json:"hourlyKeys"` } ``` `RawChineseDate` 另有一个方法 `PillarKeys() [4][2]string`, 按年、月、日、时的顺序一次给出四柱标识,正好是 [`TranslateChineseDate`](/zh/docs/go/util#translatechinesedate) 的入参形状: ```go rd := chart.RawDates fmt.Println(rd.LunarDate.LunarYear, rd.LunarDate.LunarMonth, rd.LunarDate.LunarDay, rd.LunarDate.IsLeap) fmt.Println(rd.ChineseDate.Yearly, rd.ChineseDate.YearlyKeys) fmt.Println(rd.ChineseDate.PillarKeys()) ``` **输出** ```text 2000 7 17 false [庚 辰] [gengHeavenly chenEarthly] [[gengHeavenly chenEarthly] [jiaHeavenly shenEarthly] [bingHeavenly wuEarthly] [gengHeavenly yinEarthly]] ``` | 字段 | 类型 | 说明 | | ----------- | ---------- | ---------------------- | | `TimeIndex` | `uint8` | 出生时辰索引 | | `FixLeap` | `bool` | 排盘时是否修正闰月 | | `Language` | `Language` | 盘面语言(`LanguageZhCN` 等) | | `Config` | `Config` | 排盘配置,由 DTO 还原的六个开关 | 运限、重排与 Prompt 从这四项重新发起计算,因此不必再传一遍排盘参数。 `chart.Config` 是从输出 DTO 还原的,只含六个开关;排盘时传进来的自定义四化 / 亮度表 不在里面。但星盘内部保留了调用方给的原件,因此 `Rearranged`、`Horoscope`、 Prompt 这些二次计算仍然用得上那两张表——不会静默丢失。 *** ## Palace / PalaceByIndex [#palace--palacebyindex] **用途** 按宫名、身宫、来因宫或索引取一宫。 **斗数含义** 十二宫是斗数的骨架。命宫定下后,其余十一宫按固定顺序逆时针排开。 「身宫」是十二宫之一同时被标记的那一宫,代表后天着力处; 「来因宫」是宫干与生年干相同的那一宫,代表事情的起因。 **签名** ```go func (a *Astrolabe) Palace(nameKeyOrName string) *Palace func (a *Astrolabe) PalaceByIndex(index int) *Palace ``` **参数** | 参数 | 类型 | 必填 | 默认 | 说明 | | --------------- | -------- | -- | -- | -------------------------------------------- | | `nameKeyOrName` | `string` | 是 | — | 宫名标识、`"bodyPalace"`、`"originalPalace"`,或宫名译名 | | `index` | `int` | 是 | — | 宫位索引 0–11,0 为寅宫 | **返回值** `*Palace`。名字拼错或索引越界时返回 `nil`; `"soulPalace"` 一类宫名、`"bodyPalace"`、`"originalPalace"` 只要拼对, 在任何一张盘上都定位得到。 **示例** ```go soul := chart.Palace(iztro.PalaceSoul) fmt.Println(soul.Name, soul.HeavenlyStem+soul.EarthlyBranch) fmt.Println("身宫:", chart.Palace("bodyPalace").Name) fmt.Println("来因:", chart.Palace("originalPalace").Name) fmt.Println("寅宫:", chart.PalaceByIndex(0).Name) ``` **输出** ```text 命宫 壬午 身宫: 官禄 来因: 夫妻 寅宫: 财帛 ``` **边界与陷阱** 来因宫要求宫干与生年干相同,且该宫不在子、丑二宫。 十二宫的天干由五虎遁从寅宫起排,寅到酉这十宫刚好把十天干各走一遍, 子、丑两宫重复了寅、卯的天干——正因为重复才被排除。 于是生年干在寅到酉之间必然命中且只命中一次:任何一张盘上来因宫都存在,且唯一。 身宫同理恒存在。因此 `nil` 只可能来自索引越界或名字拼错。 `chart.Palace("soulPalce")`(少一个 a)不会报错,只会返回 `nil`, 下一步取字段就 panic,错误现场离真正的笔误已经隔了一段。 用包里的 `Palace*` 常量可以让编译器与 IDE 当场挡下; 名字来自外部输入时先过一遍 [`KeyOf`](/zh/docs/go/i18n#keyof) 校验。 Go 没有联合类型,因此拆成 `Palace`(收字符串)与 `PalaceByIndex`(收整数)两个方法。 三方四正同理,有 `SurroundedPalaces` 与 `SurroundedPalacesByIndex`。 *** ## Star [#star] **用途** 按标识找到一颗星,并同时取回它所在的宫。 **签名** ```go func (a *Astrolabe) Star(keyOrName string) (*Star, *Palace) ``` **参数** | 参数 | 类型 | 必填 | 默认 | 说明 | | ----------- | -------- | -- | -- | ------- | | `keyOrName` | `string` | 是 | — | 星耀标识或译名 | **返回值** `(*Star, *Palace)`。该星不在这张盘上时两者都为 `nil`。 **示例** ```go ziwei, palace := chart.Star(iztro.StarZiweiMaj) fmt.Println(ziwei.Name, "在", palace.Name) fmt.Println("对宫是", ziwei.OppositePalace().Name) fmt.Println("亮度", ziwei.Brightness, "四化", ziwei.Mutagen) ``` **输出** ```text 紫微 在 命宫 对宫是 迁移 亮度 庙 四化 ``` 四化为空串表示这颗星没有生年四化。 **边界与陷阱** 只在主星、辅星、杂耀三组里查找。长生十二神、博士十二神、岁前与将前十二神 是每宫一个的标记而非星耀列表,用 `palace.Changsheng12Key` 一类字段直接取。 *** ## SurroundedPalaces / SurroundedPalacesByIndex [#surroundedpalaces--surroundedpalacesbyindex] **用途** 取目标宫的三方四正。 **斗数含义** 三方四正是斗数最常用的取象范围:本宫、对宫(本宫 +6)、 官禄位(本宫 +4)、财帛位(本宫 +8)。四个宫合起来看,而不只看本宫, 是因为对宫与三合宫的星耀同样作用于本宫的事。 **签名** ```go func (a *Astrolabe) SurroundedPalaces(nameKeyOrName string) *SurroundedPalaces func (a *Astrolabe) SurroundedPalacesByIndex(index int) *SurroundedPalaces ``` **返回值** `*SurroundedPalaces`,含 `Target` / `Opposite` / `Wealth` / `Career` 四个 `*Palace`。 `SurroundedPalaces` 在名字拼错时返回 `nil`;`SurroundedPalacesByIndex` 对索引取模, 因此负数与超过 11 的索引都能正确回绕,只有零值星盘(不足十二宫)才返回 `nil`。 判断方法见[三方四正](/zh/docs/go/surpalaces)。 **示例** ```go sp := chart.SurroundedPalaces(iztro.PalaceSoul) fmt.Println(sp.Target.Name, sp.Opposite.Name, sp.Wealth.Name, sp.Career.Name) fmt.Println("三方四正见紫微:", sp.Have(iztro.StarZiweiMaj)) ``` **输出** ```text 命宫 迁移 财帛 官禄 三方四正见紫微: true ``` *** ## IsSurrounded / IsSurroundedOneOf / NotSurrounded [#issurrounded--issurroundedoneof--notsurrounded] **用途** 直接在星盘上判断某宫的三方四正里有没有指定星耀,省去先取三方四正的一步。 **签名** ```go func (a *Astrolabe) IsSurrounded(nameKeyOrName string, stars ...string) bool func (a *Astrolabe) IsSurroundedOneOf(nameKeyOrName string, stars ...string) bool func (a *Astrolabe) NotSurrounded(nameKeyOrName string, stars ...string) bool ``` **参数** | 参数 | 类型 | 必填 | 默认 | 说明 | | --------------- | ----------- | -- | -- | --------- | | `nameKeyOrName` | `string` | 是 | — | 宫名标识或译名 | | `stars` | `...string` | 是 | — | 星耀标识,可变参数 | **返回值** | 方法 | 语义 | | ------------------- | ----------------- | | `IsSurrounded` | 列出的**每一颗**都在三方四正里 | | `IsSurroundedOneOf` | 列出的**至少一颗**在三方四正里 | | `NotSurrounded` | 列出的**一颗都不在**三方四正里 | **示例** ```go fmt.Println(chart.IsSurrounded(iztro.PalaceSoul, iztro.StarZiweiMaj, iztro.StarTianxiangMaj)) fmt.Println(chart.IsSurroundedOneOf(iztro.PalaceSoul, iztro.StarQishaMaj, iztro.StarPojunMaj)) fmt.Println(chart.NotSurrounded(iztro.PalaceSoul, iztro.StarHuoxingMin)) ``` **输出** ```text true false true ``` 命宫只坐紫微,天相在三方之一的财帛宫,因此第一行为真; 七杀与破军都不在这四宫内,第二行为假。 **边界与陷阱** 一颗星都不传时,`IsSurrounded` 与 `NotSurrounded` 返回 `true` (「所有元素都满足」与「没有元素不满足」对空集都成立), `IsSurroundedOneOf` 返回 `false`。 *** ## Horoscope / HoroscopeNow [#horoscope--horoscopenow] **用途** 以本盘为起点计算目标日期的运限。 **签名** ```go func (a *Astrolabe) Horoscope(targetDate string, targetTimeIndex uint8) (*Horoscope, error) func (a *Astrolabe) HoroscopeNow() (*Horoscope, error) ``` **参数** | 参数 | 类型 | 必填 | 默认 | 说明 | | ----------------- | -------- | -- | -- | -------------------- | | `targetDate` | `string` | 是 | — | 目标公历日期,格式 `YYYY-M-D` | | `targetTimeIndex` | `uint8` | 是 | — | 目标时辰索引 0–12,决定流时 | `HoroscopeNow` 取本地时钟的当前日期与当前时辰,无参数。 **返回值** `*Horoscope`——持有本盘的运限对象,六个层级的宫位查询不必再传星盘。 详见[运限对象](/zh/docs/go/horoscope)。 **示例** ```go h, _ := chart.Horoscope("2025-6-1", 0) fmt.Println("大限", h.Decadal.HeavenlyStem+h.Decadal.EarthlyBranch) fmt.Println("流年", h.Yearly.HeavenlyStem+h.Yearly.EarthlyBranch) ``` **输出** ```text 大限 庚辰 流年 乙巳 ``` *** ## 与 JSON 的关系 [#与-json-的关系] `Astrolabe` 及其下的所有类型都带 `json` 标签,标签名与 JS iztro 的字段契约一致。 因此 `json.Marshal(chart)` 直接就是可以交给前端或别的进程的 DTO: ```go b, err := json.Marshal(chart) if err != nil { log.Fatal(err) } var v map[string]any _ = json.Unmarshal(b, &v) fmt.Println(v["solarDate"], v["genderKey"], v["timeIndex"]) fmt.Println(v["palaces"].([]any)[4].(map[string]any)["nameKey"]) ``` **输出** ```text 2000-8-16 female 2 soulPalace ``` `Config` 里的自定义四化与亮度表不进 JSON——它们是排盘**输入**而非结果, 回显会破坏与 JS iztro 的字段契约。 # 宫位对象 (/zh/docs/go/palace) Palace 的字段,以及星耀判断、空宫判断与飞星族的全部方法。 宫位是斗数分析的主战场。`chart.Palace(...)` 返回 `*Palace`, 它既持有本宫数据,也能回溯所属星盘、对宫与三方四正。 ```go soul := chart.Palace(iztro.PalaceSoul) ``` 本页示例统一用 `"zh-CN"` 排盘,因此输出里的展示值都是中文。 `*Palace` 上的方法都做了 nil 接收者判断,对 `nil` 调用返回零值而不 panic; 但**取字段**仍会 panic,判空还是要做。 ## 字段 [#字段] | 字段 | 类型 | 说明 | | ------------------------------------ | ----------- | --------------------------- | | `Index` | `int` | 宫位索引 0–11,0 为寅宫 | | `Name` / `NameKey` | `string` | 宫名译名 / 标识 | | `IsBodyPalace` | `bool` | 是否身宫 | | `IsOriginalPalace` | `bool` | 是否来因宫(宫干与年干相同且不在子丑二宫) | | `HeavenlyStem` / `HeavenlyStemKey` | `string` | 宫干,决定本宫飞出的四化 | | `EarthlyBranch` / `EarthlyBranchKey` | `string` | 宫支,由索引固定:0 为寅、11 为丑 | | `MajorStars` | `[]Star` | 十四主星中落在本宫的,按安放顺序 | | `MinorStars` | `[]Star` | 十四辅星中落在本宫的 | | `AdjectiveStars` | `[]Star` | 杂耀 | | `Changsheng12` / `Changsheng12Key` | `string` | 长生十二神,每宫恰好一个 | | `Boshi12` / `Boshi12Key` | `string` | 博士十二神 | | `Jiangqian12` / `Jiangqian12Key` | `string` | 将前十二神 | | `Suiqian12` / `Suiqian12Key` | `string` | 岁前十二神 | | `Decadal` | `Decadal` | 大限:岁数区间与宫干支 | | `Ages` | `[]int` | 小限经过本宫的虚岁列表 | | `MutagenStarKeys` | `[4]string` | 本宫**宫干**化出的四颗星标识,顺序为禄、权、科、忌 | 主星、辅星、杂耀是**切片**,一宫可以有零到多颗。 长生、博士、将前、岁前十二神是**每宫恰好一个**的标记,十二宫刚好排满一轮, 因此是单值字段而不是切片。 它由**排盘时生效的**四化表算得——自定义四化表(`Config.Mutagens`)会反映在这里, 飞星族方法读的正是它。生年四化是打在星耀自身 `MutagenKey` 字段上的标记, 两者不是一回事。 ```go soul := chart.Palace(iztro.PalaceSoul) fmt.Println(soul.HeavenlyStem, soul.MutagenStarKeys) ``` **输出** ```text 壬 [tianliangMaj ziweiMaj zuofuMin wuquMaj] ``` *** ## Has / NotHave / HasOneOf [#has--nothave--hasoneof] **用途** 判断本宫坐了哪些星。 **斗数含义** 星耀落宫是斗数的基本盘面信息。「命宫坐紫微天相」即 `Has(StarZiweiMaj, StarTianxiangMaj)`。查找范围覆盖主星、辅星、杂耀三组。 **签名** ```go func (p *Palace) Has(stars ...string) bool func (p *Palace) NotHave(stars ...string) bool func (p *Palace) HasOneOf(stars ...string) bool ``` **参数** | 参数 | 类型 | 必填 | 默认 | 说明 | | ------- | ----------- | -- | -- | --------- | | `stars` | `...string` | 是 | — | 星耀标识,可变参数 | **返回值** | 方法 | 语义 | | ---------- | ---------- | | `Has` | 列出的每一颗都在本宫 | | `NotHave` | 列出的一颗都不在本宫 | | `HasOneOf` | 列出的至少一颗在本宫 | **示例** ```go soul := chart.Palace(iztro.PalaceSoul) fmt.Println(soul.Has(iztro.StarZiweiMaj, iztro.StarTianxiangMaj)) fmt.Println(soul.HasOneOf(iztro.StarQishaMaj, iztro.StarZiweiMaj)) fmt.Println(soul.NotHave(iztro.StarHuoxingMin, iztro.StarLingxingMin)) ``` **输出** ```text false true true ``` 这张盘的命宫只坐紫微,天相落在财帛宫,因此要求两颗都在的 `Has` 为假。 **边界与陷阱** `Has` 与 `NotHave` 返回 `true`,`HasOneOf` 返回 `false`。 比对的是「本宫全部星耀的标识与译名」这个集合,比不中就是没有—— `soul.Has("ziweiMj")` 返回 `false` 而不报错,与「命宫没有紫微」无法区分。 用包里的 `Star*` 常量可以让编译器与 IDE 在写错的当场挡下。 `Has` 一族同时比对 `Key` 与 `Name`,因此中文盘上 `soul.Has("紫微")` 也成立。 但这样写换语言就失效——判断请一律用标识。 *** ## HasMutagen / NotHaveMutagen [#hasmutagen--nothavemutagen] **用途** 判断本宫有没有某种四化。 **斗数含义** 本命四化由**生年干**决定,标记打在对应的星上。 一宫「有化禄」意味着这宫里坐着的某颗星被生年干化了禄。 注意这与飞星不同——飞星看的是宫干,本处看的是星上已有的标记。 **签名** ```go func (p *Palace) HasMutagen(mutagenKey string) bool func (p *Palace) NotHaveMutagen(mutagenKey string) bool ``` **参数** | 参数 | 类型 | 必填 | 默认 | 说明 | | ------------ | -------- | -- | -- | ------------------------------------------------------- | | `mutagenKey` | `string` | 是 | — | `MutagenLu` / `MutagenQuan` / `MutagenKe` / `MutagenJi` | **返回值** `bool`。 **示例** ```go children := chart.Palace(iztro.PalaceChildren) fmt.Println("子女宫有化禄:", children.HasMutagen(iztro.MutagenLu)) fmt.Println("子女宫无化忌:", children.NotHaveMutagen(iztro.MutagenJi)) ``` **输出** ```text 子女宫有化禄: true 子女宫无化忌: true ``` **边界与陷阱** `HasMutagen` 只看 `MajorStars` 与 `MinorStars` 上的四化标记,杂耀即使带标记也不计入 (复刻 iztro 的行为)。生年四化只会落在十四主星与部分辅星上, 因此实际盘面上两种口径通常没有差别。 *** ## IsEmpty [#isempty] **用途** 判断本宫是否空宫。 **斗数含义** 「空宫」指没有十四主星坐守的宫。空宫要借对宫主星来看, 是斗数里一个很常见的判断分支。辅星与杂耀默认不影响空宫的成立。 **签名** ```go func (p *Palace) IsEmpty(excludeStars ...string) bool ``` **参数** | 参数 | 类型 | 必填 | 默认 | 说明 | | -------------- | ----------- | -- | -- | ---------------------------------- | | `excludeStars` | `...string` | 否 | — | 追加计入的星耀:本宫无主星、但坐了其中任一颗时,同样**不算**空宫 | **返回值** `bool`。判定顺序是:先看有无主星,有则不空;再看 `excludeStars`,命中则不空;都不满足才是空宫。 **示例** ```go parents := chart.Palace(iztro.PalaceParents) fmt.Println("父母宫空宫:", parents.IsEmpty()) fmt.Println("仆役宫空宫:", chart.Palace(iztro.PalaceFriends).IsEmpty()) // 父母宫无主星,但坐了陀罗——把陀罗也计入后就不算空宫 fmt.Println("父母宫计入陀罗:", parents.IsEmpty(iztro.StarTuoluoMin)) ``` **输出** ```text 父母宫空宫: true 仆役宫空宫: false 父母宫计入陀罗: false ``` 这张盘只有父母、田宅两宫无主星。仆役宫坐太阴,因此不算空宫。 **边界与陷阱** `excludeStars` 不是「判断时忽略这些星」,而是「这些星也算数」。 本宫已有主星时它完全不起作用——有主星就直接不是空宫,不再看这个列表。 不传 `excludeStars` 时只检查 `MajorStars`。一宫辅星杂耀满座但没有主星,仍然是空宫。 *** ## FliesTo / FliesOneOfTo / NotFlyTo [#fliesto--fliesoneofto--notflyto] **用途** 判断本宫宫干的四化是否飞入目标宫。 **斗数含义** 飞星派的核心手法。每个宫位有自己的宫干,宫干按四化表决定 哪四颗星化禄、权、科、忌。若被化的那颗星恰好坐在目标宫,就叫「本宫化 X 入目标宫」。 「命宫化禄入财帛」表达的是命宫这件事的顺遂落在财帛上。 **签名** ```go func (p *Palace) FliesTo(to *Palace, mutagenKeys ...string) bool func (p *Palace) FliesOneOfTo(to *Palace, mutagenKeys ...string) bool func (p *Palace) NotFlyTo(to *Palace, mutagenKeys ...string) bool ``` **参数** | 参数 | 类型 | 必填 | 默认 | 说明 | | ------------- | ----------- | -- | -- | ------ | | `to` | `*Palace` | 是 | — | 目标宫对象 | | `mutagenKeys` | `...string` | 是 | — | 要检查的四化 | **返回值** | 方法 | 语义 | | -------------- | ------------------ | | `FliesTo` | 列出的四化**全部**飞入目标宫 | | `FliesOneOfTo` | 列出的四化**至少一个**飞入目标宫 | | `NotFlyTo` | 列出的四化**一个都不**飞入目标宫 | **示例** ```go soul := chart.Palace(iztro.PalaceSoul) fmt.Println("命宫化禄入财帛:", soul.FliesTo(chart.Palace(iztro.PalaceWealth), iztro.MutagenLu)) fmt.Println("命宫化禄或忌入迁移:", soul.FliesOneOfTo(chart.Palace(iztro.PalaceSurface), iztro.MutagenLu, iztro.MutagenJi)) fmt.Println("命宫不化权入子女:", soul.NotFlyTo(chart.Palace(iztro.PalaceChildren), iztro.MutagenQuan)) ``` **输出** ```text 命宫化禄入财帛: false 命宫化禄或忌入迁移: false 命宫不化权入子女: true ``` **边界与陷阱** Go 侧目标宫是 `*Palace` 而非字符串——先用 `chart.Palace(...)` 取出来再传。 传 `nil` 时三个方法都返回 `false`。 不传四化(或传空切片)时 `FliesTo` 返回 `false`, `FliesOneOfTo` 与 `NotFlyTo` 返回 `true`。 这与「空集上全称命题为真」的直觉相反,但复刻的是 iztro 的行为: `FliesTo` 先算出要找的星,一颗都没有就直接判假。 `Config.Mutagens` 换掉某个天干的四化表后,宫干落在该天干的宫飞出的星随之改变。 飞星族方法读的是排盘时生效的表,不是内置默认表。 *** ## SelfMutaged / SelfMutagedOneOf / NotSelfMutaged [#selfmutaged--selfmutagedoneof--notselfmutaged] **用途** 判断本宫是否自化。 **斗数含义** 自化指本宫宫干化出的星恰好就坐在本宫。 含义上是「自己把自己的能量释放掉」,与飞入他宫的定向作用不同。 **签名** ```go func (p *Palace) SelfMutaged(mutagenKeys ...string) bool func (p *Palace) SelfMutagedOneOf(mutagenKeys ...string) bool func (p *Palace) NotSelfMutaged(mutagenKeys ...string) bool ``` **参数** | 参数 | 类型 | 必填 | 默认 | 说明 | | ------------- | ----------- | -- | -- | ----------------------- | | `mutagenKeys` | `...string` | 否 | — | 要检查的四化;后两个方法不传时表示「四化全部」 | **返回值** | 方法 | 语义 | | ------------------ | --------------------- | | `SelfMutaged` | 列出的四化全部自化 | | `SelfMutagedOneOf` | 列出的四化至少一个自化;不传时检查全部四化 | | `NotSelfMutaged` | 列出的四化一个都不自化;不传时检查全部四化 | **示例** ```go career := chart.Palace(iztro.PalaceCareer) fmt.Println("官禄宫自化禄:", career.SelfMutaged(iztro.MutagenLu)) fmt.Println("官禄宫自化忌:", career.SelfMutaged(iztro.MutagenJi)) fmt.Println("官禄宫有任一自化:", career.SelfMutagedOneOf()) fmt.Println("官禄宫无任何自化:", career.NotSelfMutaged()) ``` **输出** ```text 官禄宫自化禄: false 官禄宫自化忌: true 官禄宫有任一自化: true 官禄宫无任何自化: false ``` 官禄宫宫干为丙,丙干化忌在廉贞,而廉贞正坐官禄宫,故成自化忌。 *** ## MutagedPlaces / MutagenStars [#mutagedplaces--mutagenstars] **用途** 取本宫宫干化出的四颗星分别落在哪些宫,或直接取那四颗星本身。 **斗数含义** 飞星分析的全景版本:不问「有没有飞到某宫」,而是一次拿到禄权科忌的落点。 **签名** ```go func (p *Palace) MutagedPlaces() []*Palace func (p *Palace) MutagenStars(mutagenKeys ...string) []string ``` **返回值** `MutagedPlaces` 返回长度为 4 的切片,顺序为**禄、权、科、忌**, 某颗被化的星不在盘上时对应位置为 `nil`。 `MutagenStars` 返回星耀标识切片,顺序与传入的四化一致。 **示例** ```go soul := chart.Palace(iztro.PalaceSoul) for i, m := range []string{"禄", "权", "科", "忌"} { if place := soul.MutagedPlaces()[i]; place != nil { fmt.Printf("化%s → %s\n", m, place.Name) } else { fmt.Printf("化%s → 不在盘上\n", m) } } fmt.Println(soul.MutagenStars(iztro.MutagenLu, iztro.MutagenJi)) ``` **输出** ```text 化禄 → 子女 化权 → 命宫 化科 → 官禄 化忌 → 财帛 [tianliangMaj wuquMaj] ``` 命宫宫干为壬,壬干四化为天梁化禄、紫微化权、左辅化科、武曲化忌。 *** ## OppositePalace / SurroundedPalaces / Astrolabe [#oppositepalace--surroundedpalaces--astrolabe] **用途** 从宫位回溯到对宫、三方四正与所属星盘。 **签名** ```go func (p *Palace) OppositePalace() *Palace func (p *Palace) SurroundedPalaces() *SurroundedPalaces func (p *Palace) Astrolabe() *Astrolabe ``` **返回值** 脱离星盘单独构造的宫位返回 `nil`;由星盘查询得到的宫位必然非空。 **示例** ```go soul := chart.Palace(iztro.PalaceSoul) fmt.Println(soul.Name, "的对宫是", soul.OppositePalace().Name) fmt.Println("三方四正见煞:", soul.SurroundedPalaces().HaveOneOf(iztro.StarHuoxingMin, iztro.StarLingxingMin)) fmt.Println(soul.Astrolabe().FiveElementsClass) ``` **输出** ```text 命宫 的对宫是 迁移 三方四正见煞: true 木三局 ``` # 星耀对象 (/zh/docs/go/star-object) Star 的字段与亮度、四化判断,以及回溯所在宫的能力。 `Star` 是一颗落在某宫的星,带着它的类型、亮度与四化标记,并能回溯所在宫。 ```go ziwei, palace := chart.Star(iztro.StarZiweiMaj) ``` 本页示例统一用 `"zh-CN"` 排盘,因此输出里的展示值都是中文。 ## 字段 [#字段] | 字段 | 类型 | 说明 | | --------------- | -------- | ------------------------------ | | `Key` | `string` | 星耀标识,与语言无关,判断时用它 | | `Name` | `string` | 星名,按排盘语言翻译 | | `Type` | `string` | 星耀类型,见下表 | | `Scope` | `string` | 作用范围:本命星为 `"origin"`,流耀为对应运限层级 | | `Brightness` | `string` | 亮度译名;没有亮度表的星耀为空串 | | `BrightnessKey` | `string` | 亮度标识 | | `Mutagen` | `string` | 生年四化译名;未被生年干化的星为空串 | | `MutagenKey` | `string` | 四化标识 | ### 星耀类型的八个取值 [#星耀类型的八个取值] | 取值 | 含义 | 典型成员 | | ----------- | ---- | ----------------- | | `major` | 十四主星 | 紫微、天府、七杀、破军 | | `soft` | 吉星 | 左辅、右弼、文昌、文曲、天魁、天钺 | | `tough` | 煞星 | 擎羊、陀罗、火星、铃星、地空、地劫 | | `adjective` | 杂耀 | 三台、八座、天刑、天姚 | | `flower` | 桃花星 | 红鸾、天喜、咸池 | | `helper` | 解神 | 解神 | | `lucun` | 禄存 | 禄存 | | `tianma` | 天马 | 天马 | 禄存与天马各自独占一类,因为它们在传统分法里既非纯吉也非纯煞,判断时常单独拎出来。 Go 侧用空串表示「没有」——`Brightness` 为空串即该星没有亮度表, `Mutagen` 为空串即未被生年干化。判空写 `if star.MutagenKey != ""`。 *** ## WithBrightness [#withbrightness] **用途** 判断这颗星是否处于给定亮度之一。 **斗数含义** 亮度(庙旺得利平不陷)描述星耀在该宫位的强弱。 同一颗星在十二宫各有定值,庙旺则力量充分发挥,落陷则受制。 **签名** ```go func (s *Star) WithBrightness(brightnessKeys ...string) bool ``` **参数** | 参数 | 类型 | 必填 | 默认 | 说明 | | ---------------- | ----------- | -- | -- | ------------ | | `brightnessKeys` | `...string` | 是 | — | 亮度标识,命中任一即为真 | **返回值** `bool`。该星无亮度时恒为假。 **示例** ```go ziwei, _ := chart.Star(iztro.StarZiweiMaj) fmt.Println(ziwei.WithBrightness(iztro.BrightnessMiao)) fmt.Println(ziwei.WithBrightness(iztro.BrightnessWang, iztro.BrightnessDe)) ``` **输出** ```text true false ``` **边界与陷阱** 语义是「命中任一」而非「全部命中」——一颗星只有一个亮度, 传多个只表示「是其中之一即可」。 *** ## WithMutagen [#withmutagen] **用途** 判断这颗星是否带指定的生年四化。 **斗数含义** 生年四化由出生年干决定,一年固定四颗星分别化禄、权、科、忌。 这个标记跟着星走,无论那颗星落在哪一宫。 **签名** ```go func (s *Star) WithMutagen(mutagenKeys ...string) bool ``` **参数** | 参数 | 类型 | 必填 | 默认 | 说明 | | ------------- | ----------- | -- | -- | ------------ | | `mutagenKeys` | `...string` | 是 | — | 四化标识,命中任一即为真 | **返回值** `bool`。该星未被生年干化时恒为假。 **示例** ```go ziwei, _ := chart.Star(iztro.StarZiweiMaj) taiyang, _ := chart.Star(iztro.StarTaiyangMaj) fmt.Println("紫微化禄:", ziwei.WithMutagen(iztro.MutagenLu)) fmt.Println("太阳化禄:", taiyang.WithMutagen(iztro.MutagenLu)) ``` **输出** ```text 紫微化禄: false 太阳化禄: true ``` 这张盘生年干为庚,庚干太阳化禄,因此标记落在太阳而非紫微。 **边界与陷阱** `WithMutagen` 看的是**生年干**给这颗星打的标记,一张盘上只有四颗星带标记。 宫干飞出的四化不在这里体现,用宫位的 [`FliesTo`](/zh/docs/go/palace#fliesto--fliesoneofto--notflyto) 一族。 *** ## Palace / OppositePalace / SurroundedPalaces [#palace--oppositepalace--surroundedpalaces] **用途** 从星回溯到它所在的宫、该宫的对宫与三方四正。 **签名** ```go func (s *Star) Palace() *Palace func (s *Star) OppositePalace() *Palace func (s *Star) SurroundedPalaces() *SurroundedPalaces ``` **返回值** 脱离星盘单独构造的星耀返回 `nil`;由星盘查询得到的星耀必然非空。 **示例** ```go ziwei, _ := chart.Star(iztro.StarZiweiMaj) fmt.Println(ziwei.Palace().Name) fmt.Println(ziwei.OppositePalace().Name) fmt.Println("同宫或三方见天相:", ziwei.SurroundedPalaces().Have(iztro.StarTianxiangMaj)) ``` **输出** ```text 命宫 迁移 同宫或三方见天相: true ``` **边界与陷阱** `chart.Star()` 已经把所在宫作为第二个返回值给出, 通常不必再调 `ziwei.Palace()`。 # 三方四正 (/zh/docs/go/surpalaces) SurroundedPalaces 的四个宫位与五个判断方法。 三方四正是斗数最常用的取象范围。看一件事不能只看本宫, 对宫与两个三合宫的星耀同样作用其上,四宫合看才完整。 本页示例统一用 `"zh-CN"` 排盘,因此输出里的展示值都是中文。 ## 四个宫位 [#四个宫位] | 字段 | 相对本宫 | 传统称呼 | 意义 | | ---------- | ---- | ---- | -------------- | | `Target` | +0 | 本宫 | 事情本身 | | `Opposite` | +6 | 对宫 | 与本宫相对的一面,影响最直接 | | `Career` | +4 | 官禄位 | 三合之一 | | `Wealth` | +8 | 财帛位 | 三合之一 | 四个字段都是 `*Palace`,[宫位对象](/zh/docs/go/palace)的全部方法都能用。 `Wealth` 与 `Career` 指的是「相对本宫的三合位置」,不是十二宫里那两个固定的宫名。 以命宫起算时它们恰好落在财帛宫与官禄宫(+8 与 +4),名字就是这么对上的; 以别的宫起算则是别的宫。 ## 四种取法 [#四种取法] ```go // 从星盘按宫名取 byName := chart.SurroundedPalaces(iztro.PalaceSoul) // 从星盘按索引取 byIndex := chart.SurroundedPalacesByIndex(4) // 从宫位取 fromPalace := chart.Palace(iztro.PalaceSoul).SurroundedPalaces() // 从星耀取(该星所在宫的三方四正) ziwei, _ := chart.Star(iztro.StarZiweiMaj) fromStar := ziwei.SurroundedPalaces() fmt.Println(byName.Target.Name, byIndex.Target.Name, fromPalace.Target.Name, fromStar.Target.Name) ``` **输出** ```text 命宫 命宫 命宫 命宫 ``` 命宫落在索引 4、紫微又正坐命宫,因此四种取法在这张盘上给出同一组三方四正; 选哪个取决于手上已有什么。 `SurroundedPalacesByIndex` 对索引取模,因此 `-1`、`12` 都能正确回绕; 但**零值星盘**(未经排盘构造出来的 `Astrolabe`)没有十二宫,此时返回 `nil`。 `SurroundedPalaces` 收名字,拼错返回 `nil`。两者都要判空再取字段。 *** ## Have / NotHave / HaveOneOf [#have--nothave--haveoneof] **用途** 判断四宫合起来有没有指定星耀。 **斗数含义** 「三方四正见紫微」这类说法,问的正是这四宫里出没出现某颗星, 而不问具体落在其中哪一宫。 **签名** ```go func (sp *SurroundedPalaces) Have(stars ...string) bool func (sp *SurroundedPalaces) NotHave(stars ...string) bool func (sp *SurroundedPalaces) HaveOneOf(stars ...string) bool ``` **参数** | 参数 | 类型 | 必填 | 默认 | 说明 | | ------- | ----------- | -- | -- | --------- | | `stars` | `...string` | 是 | — | 星耀标识,可变参数 | **返回值** | 方法 | 语义 | | ----------- | -------------------- | | `Have` | 列出的每一颗都出现在这四宫(不要求同宫) | | `NotHave` | 列出的一颗都没出现 | | `HaveOneOf` | 列出的至少一颗出现 | **示例** ```go sp := chart.SurroundedPalaces(iztro.PalaceSoul) fmt.Println(sp.Have(iztro.StarZiweiMaj, iztro.StarTianxiangMaj)) fmt.Println(sp.HaveOneOf(iztro.StarQishaMaj, iztro.StarPojunMaj)) fmt.Println(sp.NotHave(iztro.StarHuoxingMin)) ``` **输出** ```text true false true ``` 紫微在命宫、天相在财帛宫,分处两宫但都在这四宫内,因此 `Have` 为真。 **边界与陷阱** `Have(A, B)` 的语义是「A 和 B 都出现在这四宫里」, 不要求它们坐在同一宫。要判断同宫,用宫位的 [`Has`](/zh/docs/go/palace#has--nothave--hasoneof)。 `Have` 与 `NotHave` 返回 `true`,`HaveOneOf` 返回 `false`。 *** ## HaveMutagen / NotHaveMutagen [#havemutagen--nothavemutagen] **用途** 判断四宫里有没有某种生年四化。 **斗数含义** 「三方四正见忌」意味着这组宫位里坐着一颗被生年干化忌的星, 是判断压力来源的常用条件。 **签名** ```go func (sp *SurroundedPalaces) HaveMutagen(mutagenKey string) bool func (sp *SurroundedPalaces) NotHaveMutagen(mutagenKey string) bool ``` **参数** | 参数 | 类型 | 必填 | 默认 | 说明 | | ------------ | -------- | -- | -- | ------ | | `mutagenKey` | `string` | 是 | — | 四化标识之一 | **返回值** `bool`。 **示例** ```go sp := chart.SurroundedPalaces(iztro.PalaceSoul) fmt.Println("三方四正见禄:", sp.HaveMutagen(iztro.MutagenLu)) fmt.Println("三方四正见忌:", sp.HaveMutagen(iztro.MutagenJi)) fmt.Println("三方四正不见科:", sp.NotHaveMutagen(iztro.MutagenKe)) ``` **输出** ```text 三方四正见禄: false 三方四正见忌: false 三方四正不见科: true ``` 这张盘的生年四化落在子女、迁移、疾厄三宫,都不在命宫的三方四正内。 **边界与陷阱** 这里看的是**生年四化**打在星上的标记,与宫干飞出的四化无关。 后者请用宫位的飞星族方法。 # 运限对象 (/zh/docs/go/horoscope) 六个运限层级的数据结构,以及不必再传星盘的宫位查询方法。 运限把本命盘投影到某个时间点上。同一张盘,不同年份看到的宫位分布不同—— 这正是「大限走到哪一宫」的意思。 ```go h, _ := chart.Horoscope("2025-6-1", 0) ``` `Horoscope` 持有发起它的那张本命盘,因此所有查询方法都不必再把星盘传进去。 本页示例统一用 `"zh-CN"` 的本命盘,因此输出里的展示值都是中文。 ## 字段 [#字段] | 字段 | 类型 | 跨度 | 说明 | | ------------------------- | ---------------- | --- | ----------------------------------------------------------- | | `SolarDate` / `LunarDate` | `string` | — | **目标**日期的公历串与农历中文写法。出生日期在本命盘上,用 `h.Astrolabe().SolarDate` 取 | | `Decadal` | `HoroscopeScope` | 十年 | 大限。未起运的幼年期为童限 | | `Age` | `HoroscopeScope` | 一年 | 小限。按虚岁逐年走一宫 | | `Yearly` | `HoroscopeScope` | 一年 | 流年。按流年干支定宫 | | `Monthly` | `HoroscopeScope` | 一月 | 流月 | | `Daily` | `HoroscopeScope` | 一日 | 流日 | | `Hourly` | `HoroscopeScope` | 一时辰 | 流时 | 两者都是一年一走,但起法不同:小限从生年地支起、按虚岁顺推, 流年直接看那一年的干支落在哪一宫。两条线互相独立,斗数里通常并看。 ### HoroscopeScope [#horoscopescope] | 字段 | 类型 | 说明 | | ------------------------------------ | ---------------- | --------------------------------- | | `Index` | `int` | 该层级落在哪一宫(宫位索引) | | `Name` | `string` | 层级显示名,按输出语言翻译 | | `HeavenlyStem` / `HeavenlyStemKey` | `string` | 该层级的天干,决定它飞出的四化 | | `EarthlyBranch` / `EarthlyBranchKey` | `string` | 该层级的地支 | | `PalaceNames` / `PalaceNameKeys` | `[]string` | 以该层级所在宫为命宫重推的十二宫名,按宫位索引排列 | | `Mutagen` / `MutagenKeys` | `[]string` | 该层级天干引发的四化星,顺序为禄权科忌 | | `Stars` | `[][]Star` | 该层级的流耀分布;无流耀的层级为 `nil` | | `NominalAge` | `int` | 仅小限:虚岁。其余层级为 `0` | | `YearlyDecStar` | `*YearlyDecStar` | 仅流年:岁前与将前十二神。其余层级为 **`nil`** | ```go type YearlyDecStar struct { Suiqian12 []string `json:"suiqian12"` // 岁前十二神译名,按宫位索引排列 Suiqian12Keys []string `json:"suiqian12Keys"` // 对应标识 Jiangqian12 []string `json:"jiangqian12"` // 将前十二神译名 Jiangqian12Keys []string `json:"jiangqian12Keys"` // 对应标识 } ``` Go 侧不为小限与流年各开一个类型,而是把 `NominalAge` 与 `YearlyDecStar` 放进共用的 `HoroscopeScope`——其余层级上这两项为零值。 调用处因此可以写按层级参数化的通用逻辑。 `h.Decadal.YearlyDecStar` 是 `nil`,直接取字段会 panic。 只有 `h.Yearly.YearlyDecStar` 非空——按层级遍历时先判空。 ```go h, _ := chart.Horoscope("2025-6-1", 0) fmt.Println(h.Decadal.YearlyDecStar == nil, h.Yearly.YearlyDecStar != nil) fmt.Println(h.Yearly.YearlyDecStar.Suiqian12[:3]) fmt.Println(h.Yearly.YearlyDecStar.Jiangqian12Keys[:3]) fmt.Println(h.Decadal.NominalAge, h.Age.NominalAge) ``` **输出** ```text true true [天德 吊客 病符] [jiesha zhaisha tiansha] 0 26 ``` ### PalaceIndexByName [#palaceindexbyname] ```go func (item *HoroscopeScope) PalaceIndexByName(nameKeyOrName string) int ``` 在该层级重排后的十二宫里,按宫名标识或当前语言宫名查宫位索引;**找不到返回 -1**。 ```go h, _ := chart.Horoscope("2025-6-1", 0) fmt.Println(h.Decadal.PalaceIndexByName(iztro.PalaceSoul)) fmt.Println(h.Decadal.PalaceIndexByName(iztro.PalaceWealth)) fmt.Println(h.Decadal.PalaceIndexByName("nosuch")) ``` **输出** ```text 2 10 -1 ``` 返回 `-1` 而不是 `0`——`0` 是合法的宫位索引(寅宫)。 拿它去索引 `chart.Palaces` 前务必判负。 **示例** ```go h, _ := chart.Horoscope("2025-6-1", 0) for _, item := range []iztro.HoroscopeScope{h.Decadal, h.Monthly, h.Daily, h.Hourly} { fmt.Printf("%s 落在宫位 %d 干支 %s%s\n", item.Name, item.Index, item.HeavenlyStem, item.EarthlyBranch) } fmt.Println("小限虚岁", h.Age.NominalAge) fmt.Println("大限四化", h.Decadal.Mutagen) fmt.Println("流年岁前十二神", h.Yearly.YearlyDecStar.Suiqian12[:3]) ``` **输出** ```text 大限 落在宫位 2 干支 庚辰 流月 落在宫位 3 干支 壬午 流日 落在宫位 8 干支 辛丑 流时 落在宫位 8 干支 戊子 小限虚岁 26 大限四化 [太阳 武曲 太阴 天同] 流年岁前十二神 [天德 吊客 病符] ``` *** ## AgePalace [#agepalace] **用途** 取小限当年所在的宫。 **斗数含义** 小限是逐年推移的一条线,落在哪一宫就以那宫为该年重点。 **签名** ```go func (h *Horoscope) AgePalace() *Palace ``` **返回值** `*Palace`——本命盘上的宫位。 **示例** ```go h, _ := chart.Horoscope("2025-6-1", 0) fmt.Println(h.AgePalace().Name) ``` **输出** ```text 田宅 ``` *** ## Palace [#palace] **用途** 取某个运限层级下、按该层级重推的十二宫中的某一宫。 **斗数含义** 大限走到某宫后,以那一宫为「大限命宫」重排十二宫。 「大限的夫妻宫」问的就是这套重排后的宫位,与本命夫妻宫通常不是同一宫。 **签名** ```go func (h *Horoscope) Palace(nameKeyOrName string, scope string) *Palace ``` **参数** | 参数 | 类型 | 必填 | 默认 | 说明 | | --------------- | -------- | -- | -- | ----------- | | `nameKeyOrName` | `string` | 是 | — | 要取的宫名标识或译名 | | `scope` | `string` | 是 | — | 在哪个层级的十二宫里找 | **返回值** `*Palace`——本命盘上的宫位(同一格宫位在不同层级有不同宫名)。 层级为 `ScopeOrigin` 时即本命十二宫。查不到返回 `nil`。 **示例** ```go h, _ := chart.Horoscope("2025-6-1", 0) fmt.Println("大限命宫落在本命的", h.Palace(iztro.PalaceSoul, iztro.ScopeDecadal).Name) fmt.Println("本命命宫是", h.Palace(iztro.PalaceSoul, iztro.ScopeOrigin).Name) ``` **输出** ```text 大限命宫落在本命的 夫妻 本命命宫是 命宫 ``` **边界与陷阱** 返回的宫位对象上,`Name` 仍是**本命宫名**(例中的夫妻),因为它就是本命盘上的那一格。 要看该格在大限层级叫什么,查 `h.Decadal.PalaceNames[index]`。 *** ## SurroundPalaces [#surroundpalaces] **用途** 取某个运限层级下某宫的三方四正。 **签名** ```go func (h *Horoscope) SurroundPalaces(nameKeyOrName string, scope string) *SurroundedPalaces ``` **参数** 同 `Palace`。 **返回值** `*SurroundedPalaces`,判断方法见[三方四正](/zh/docs/go/surpalaces)。 **示例** ```go h, _ := chart.Horoscope("2025-6-1", 0) sp := h.SurroundPalaces(iztro.PalaceWealth, iztro.ScopeYearly) fmt.Println("流年财帛的三方四正以本命", sp.Target.Name, "为本宫") ``` **输出** ```text 流年财帛的三方四正以本命 疾厄 为本宫 ``` *** ## HasHoroscopeStars / HasOneOfHoroscopeStars / NotHaveHoroscopeStars [#hashoroscopestars--hasoneofhoroscopestars--nothavehoroscopestars] **用途** 判断某层级某宫里有没有指定的流耀。 **斗数含义** 流耀是随运限层级产生的一组星:魁钺昌曲禄羊陀马鸾喜。 它们在不同层级有不同名字——大限层级叫运魁、运钺,流年层级叫流魁、流钺, 含义相同但作用于各自的时间跨度。 **签名** ```go func (h *Horoscope) HasHoroscopeStars(nameKeyOrName string, scope string, stars []string) bool func (h *Horoscope) HasOneOfHoroscopeStars(nameKeyOrName string, scope string, stars []string) bool func (h *Horoscope) NotHaveHoroscopeStars(nameKeyOrName string, scope string, stars []string) bool ``` **参数** | 参数 | 类型 | 必填 | 默认 | 说明 | | --------------- | ---------- | -- | -- | --------------- | | `nameKeyOrName` | `string` | 是 | — | 该层级下的宫名 | | `scope` | `string` | 是 | — | 运限层级 | | `stars` | `[]string` | 是 | — | 流耀标识切片,须用该层级的名字 | **返回值** | 方法 | 语义 | | ------------------------ | ----- | | `HasHoroscopeStars` | 每一颗都在 | | `HasOneOfHoroscopeStars` | 至少一颗在 | | `NotHaveHoroscopeStars` | 一颗都不在 | **示例** ```go h, _ := chart.Horoscope("2025-6-1", 0) fmt.Println(h.HasHoroscopeStars(iztro.PalaceSoul, iztro.ScopeDecadal, []string{"yunlu"})) fmt.Println(h.HasOneOfHoroscopeStars(iztro.PalaceSoul, iztro.ScopeDecadal, []string{"yunlu", "yunyang"})) fmt.Println(h.NotHaveHoroscopeStars(iztro.PalaceSoul, iztro.ScopeDecadal, []string{"yuntuo"})) ``` **输出** ```text false false true ``` **边界与陷阱** 这三个方法前面已有宫名与层级两个字符串参数,再用可变参数会让调用处产生歧义, 因此星耀参数取 `[]string`。包里其余带星耀列表的方法都是可变参数。 三个方法用 `scope` + 宫名定位到本命盘上的某一格, 但要比对的星耀集合恒为**大限流耀与流年流耀的并集**,与 `scope` 无关。 因此 `scope` 传 `ScopeMonthly` 时,查的是「流月某宫这一格里有没有大限或流年的流耀」, 而不是流月自己的流耀——流月、流日、流时三层的流耀不参与这里的比对。 要按层级取流耀分布,用 `h.Monthly.Stars`,或 [`GetHoroscopeStar`](/zh/docs/go/star#gethoroscopestar)。 大限流耀叫 `StarYunlu`(运禄)、`StarYunyang`(运羊)……,流年流耀叫 `StarLiulu`(流禄)、`StarLiuyang`(流羊)……,两组标识不同名。 由于比对集合恒是这两组的并集,两组标识在任何 `scope` 下都查得到,只是落宫不同。 各层级的标识对照见[安星模块](/zh/docs/go/star#gethoroscopestar)。 *** ## HasHoroscopeMutagen [#hashoroscopemutagen] **用途** 判断某层级某宫里有没有该层级天干引发的四化。 **斗数含义** 每个运限层级有自己的天干,会像生年干一样化出四颗星。 「大限化禄落在大限财帛」这类判断问的就是这个。 **签名** ```go func (h *Horoscope) HasHoroscopeMutagen(nameKeyOrName string, scope string, mutagenKey string) bool ``` **参数** | 参数 | 类型 | 必填 | 默认 | 说明 | | --------------- | -------- | -- | -- | ------- | | `nameKeyOrName` | `string` | 是 | — | 该层级下的宫名 | | `scope` | `string` | 是 | — | 运限层级 | | `mutagenKey` | `string` | 是 | — | 四化标识 | **返回值** `bool`。检查该层级天干化出的那颗星是否落在目标宫的主星或辅星里(不看杂耀)。 **示例** ```go h, _ := chart.Horoscope("2025-6-1", 0) fmt.Println(h.HasHoroscopeMutagen(iztro.PalaceSoul, iztro.ScopeDecadal, iztro.MutagenLu)) fmt.Println(h.Decadal.Mutagen) ``` **输出** ```text false [太阳 武曲 太阴 天同] ``` 大限干为庚,庚干四化为太阳化禄、武曲化权、太阴化科、天同化忌。 **边界与陷阱** 本命层级没有「层级天干」这回事——生年四化已经打在星耀自身的 `MutagenKey` 上。 `HasHoroscopeMutagen(name, iztro.ScopeOrigin, m)` 因此直接返回 `false`, 不代表本命盘上没有这个四化。要查本命四化,用宫位的 [`HasMutagen`](/zh/docs/go/palace#hasmutagen--nothavemutagen)。 *** ## ScopeItem / Astrolabe [#scopeitem--astrolabe] **用途** 按层级标识取对应的 `HoroscopeScope`,或回到本命盘。 **签名** ```go func (h *Horoscope) ScopeItem(scope string) *HoroscopeScope func (h *Horoscope) Astrolabe() *Astrolabe ``` **返回值** `ScopeItem` 在层级为 `ScopeOrigin` 或未知层级时返回 `nil`——本命不是运限层级。 **示例** ```go h, _ := chart.Horoscope("2025-6-1", 0) fmt.Println(h.ScopeItem(iztro.ScopeDecadal).Name) fmt.Println(h.ScopeItem(iztro.ScopeOrigin)) fmt.Println(h.Astrolabe().SolarDate) ``` **输出** ```text 大限 2000-8-16 ``` **边界与陷阱** `ScopeItem` 用于写按层级参数化的通用逻辑,比一串 `switch scope` 简洁。 返回 `nil` 时记得判空。 # 轻量查询 (/zh/docs/go/query) 不排整盘就能拿到的生肖、星座与命宫主星。 有些问题不需要整张星盘。这五个函数各自只跑到必要的那一步就返回, 结果与完整排盘的对应字段永远一致——它们走的是同一套核心逻辑。 本页示例统一用 `"zh-CN"` 排盘,因此输出里的展示值都是中文。 *** ## GetZodiacBySolarDate [#getzodiacbysolardate] **用途** 由公历日期取生肖。 **斗数含义** 生肖由**年支**决定,而年支的换算时点受 `YearDivide` 影响。 正月初一与立春之间出生的人,两种配置会得到不同的生肖——这不是缺陷,是流派差异。 **签名** ```go func GetZodiacBySolarDate(solarDate string, language Language, config *Config) (string, error) ``` **参数** | 参数 | 类型 | 必填 | 默认 | 说明 | | ----------- | ---------- | -- | -- | ------------------------------- | | `solarDate` | `string` | 是 | — | 公历日期,格式 `YYYY-M-D` | | `language` | `Language` | 是 | — | 盘面语言 | | `config` | `*Config` | 是 | — | 传 `nil` 取默认;仅 `YearDivide` 影响结果 | **返回值** 按语言翻译的生肖名。 **示例** ```go zodiac, _ := iztro.GetZodiacBySolarDate("2000-8-16", iztro.LanguageZhCN, nil) fmt.Println(zodiac) ``` **输出** ```text 龙 ``` **边界与陷阱** 默认按正月初一换年。改成 `&Config{YearDivide: iztro.YearDivideExact}` 后按立春换年, 1 月下旬到 2 月上旬出生的人可能拿到不同生肖。 *** ## GetSignBySolarDate / GetSignByLunarDate [#getsignbysolardate--getsignbylunardate] **用途** 取星座。 **斗数含义** 星座是西洋占星概念,只由公历日期决定,与斗数算法无关。 农历版本先把农历转成公历再判定,因此两者对同一天的结果相同。 **签名** ```go func GetSignBySolarDate(solarDate string, language Language) (string, error) func GetSignByLunarDate(lunarDate string, isLeapMonth bool, language Language) (string, error) ``` **参数** | 参数 | 类型 | 必填 | 默认 | 说明 | | ------------------------- | ---------- | -- | -- | ---------------- | | `solarDate` / `lunarDate` | `string` | 是 | — | 日期,格式 `YYYY-M-D` | | `isLeapMonth` | `bool` | 是 | — | 仅农历版本:该月是否闰月 | | `language` | `Language` | 是 | — | 盘面语言 | 无 `config` 参数——星座不受任何配置影响。 **返回值** 星座名。 **示例** ```go s1, _ := iztro.GetSignBySolarDate("2000-8-16", iztro.LanguageZhCN) s2, _ := iztro.GetSignByLunarDate("2000-7-17", false, iztro.LanguageZhCN) fmt.Println(s1, s2) ``` **输出** ```text 狮子座 狮子座 ``` *** ## GetMajorStarBySolarDate / GetMajorStarByLunarDate [#getmajorstarbysolardate--getmajorstarbylunardate] **用途** 只取命宫主星,不排整盘。 **斗数含义** 命宫主星是斗数最常被单独问起的一项。 命宫为空宫时按惯例借对宫主星来看,本函数已经处理了这一步。 **签名** ```go func GetMajorStarBySolarDate( solarDate string, timeIndex uint8, fixLeap bool, language Language, config *Config, ) (string, error) func GetMajorStarByLunarDate( lunarDate string, timeIndex uint8, leap LeapMonth, language Language, config *Config, ) (string, error) ``` **参数** | 参数 | 类型 | 必填 | 默认 | 说明 | | ------------------------- | ----------- | -- | -- | -------------------------------------------------------------------------------------------------- | | `solarDate` / `lunarDate` | `string` | 是 | — | 日期 | | `timeIndex` | `uint8` | 是 | — | 时辰索引 0–12,命宫由月份与时辰共同决定 | | `fixLeap` | `bool` | 是 | — | 仅阳历版本:阳历日期落在闰月十五之后时是否视作次月 | | `leap` | `LeapMonth` | 是 | — | 仅农历版本:`NotLeapMonth` / `LeapMonthKeep` / `LeapMonthFixed`,见 [`ByLunar`](/zh/docs/go/astro#bylunar) | | `language` | `Language` | 是 | — | 盘面语言 | | `config` | `*Config` | 是 | — | 传 `nil` 取默认 | **返回值** 多颗主星以逗号分隔;空宫时返回对宫主星。 **示例** ```go zh, _ := iztro.GetMajorStarBySolarDate("2000-8-16", 2, true, iztro.LanguageZhCN, nil) en, _ := iztro.GetMajorStarBySolarDate("2000-8-16", 2, true, iztro.LanguageEnUS, nil) fmt.Println(zh, en) ``` **输出** ```text 紫微 emperor ``` **边界与陷阱** 命宫由农历月份与出生时辰共同定位,因此 `timeIndex` 是必填的。 只知道日期不知道时辰时,斗数无法给出确定的命宫。 返回值是翻译后的字符串,换语言就会变。要做程序判断请排整盘, 用 `chart.Palace(iztro.PalaceSoul)` 取宫位后比较 `MajorStars` 里的 `Key`。 # 工具函数 (/zh/docs/go/util) 索引换算、亮度与四化查表、命身宫推算、大限小限、四柱展示串。 这些函数是排盘算法的零件。自己实现斗数逻辑、或要复核某一步推算时用得上; 日常排盘不必直接调用。 参数与返回值中的标识都与语言无关,可直接与星盘上的 `*Key` 字段互操作。 *** ## FixIndex / FixIndex12 [#fixindex--fixindex12] **用途** 把任意整数约束到循环区间。 **斗数含义** 十二宫首尾相接,从丑宫(索引 11)再走一格回到寅宫(索引 0)。 所有「顺数几格、逆数几格」的推算都靠这个回绕。 **签名** ```go func FixIndex(index int, max int) (int, error) func FixIndex12(index int) int ``` **参数** | 参数 | 类型 | 必填 | 默认 | 说明 | | ------- | ----- | -- | -- | -------------------------------- | | `index` | `int` | 是 | — | 待修正的索引,可为负 | | `max` | `int` | 是 | — | 循环长度;传 `0` 取默认值 12,天干用 10。负数返回错误 | **返回值** 落在 `0..max` 的索引(含 0,不含 `max`)。 `FixIndex12` 固定模 12、不返回错误——十二宫回绕直接用它。 **示例** ```go a, _ := iztro.FixIndex(-1, 0) b, _ := iztro.FixIndex(13, 0) c, _ := iztro.FixIndex(11, 10) fmt.Println(a, b, c) fmt.Println(iztro.FixIndex12(-1), iztro.FixIndex12(13)) _, err := iztro.FixIndex(0, -1) fmt.Println(err) ``` **输出** ```text 11 1 1 11 1 iztro: invalid max '-1': expected a positive integer ``` **边界与陷阱** 这是复刻 iztro `fixIndex(index, max = 12)` 默认参数的写法,与 Go 的零值直觉相反: `FixIndex(13, 0)` 得到 1 而不是报错。十二宫回绕请直接用 `FixIndex12`, 省掉这个歧义与那个永远不会发生的 `error`。 负数按数学取模回绕(-1 → 11),不是截断到 0。 这两个函数在 Go 侧直接算,不往返 wasm。 *** ## EarthlyBranchToPalaceIndex [#earthlybranchtopalaceindex] **用途** 地支转宫位索引。 **斗数含义** 十二宫的排列从**寅宫**起,而地支的自然顺序从**子**起,两者差两格。 这个函数负责这层换算:寅 → 0,卯 → 1,⋯,子 → 10,丑 → 11。 **签名** ```go func EarthlyBranchToPalaceIndex(branchKey string) (int, error) ``` **返回值** `int`,0–11。 **示例** ```go yin, _ := iztro.EarthlyBranchToPalaceIndex(iztro.BranchYin) zi, _ := iztro.EarthlyBranchToPalaceIndex(iztro.BranchZi) fmt.Println(yin, zi) ``` **输出** ```text 0 10 ``` *** ## TimeToIndex [#timetoindex] **用途** 小时数转时辰索引。 **斗数含义** 一天十二时辰,每时辰两小时,但子时横跨午夜被拆成早子时(0)与晚子时(12), 因此索引有 13 个值。 **签名** ```go func TimeToIndex(hour uint8) (uint8, error) ``` **参数** | 参数 | 类型 | 必填 | 默认 | 说明 | | ------ | ------- | -- | -- | --------------- | | `hour` | `uint8` | 是 | — | 小时数 0–23,越界返回错误 | **返回值** `uint8`,0–12——正好是排盘入口 `timeIndex` 参数的类型,可以直接传过去。 **示例** ```go a, _ := iztro.TimeToIndex(0) b, _ := iztro.TimeToIndex(4) c, _ := iztro.TimeToIndex(23) fmt.Println(a, b, c) _, err := iztro.TimeToIndex(24) fmt.Println(err) // 结果可直接喂给排盘入口 chart, err := iztro.BySolar("2000-8-16", b, iztro.GenderFemale, true, iztro.LanguageZhCN, nil) if err != nil { log.Fatal(err) } fmt.Println(chart.Time) ``` **输出** ```text 0 2 12 iztro: invalid hour '24': expected 0-23 寅时 ``` 0 点为早子时,4 点为寅时,23 点为晚子时。排盘时不确定时辰索引,用这个函数换算。 *** ## GetAgeIndex [#getageindex] **用途** 由生年地支取小限起始宫位索引。 **斗数含义** 小限从固定的宫起,按虚岁逐年推移。起宫由生年地支所属的三合组决定: 寅午戌年起辰宫、申子辰年起戌宫、巳酉丑年起未宫、亥卯未年起丑宫。 **签名** ```go func GetAgeIndex(branchKey string) (int, error) ``` **返回值** `int`,0–11。 **示例** ```go idx, _ := iztro.GetAgeIndex(iztro.BranchChen) fmt.Println(idx) ``` **输出** ```text 8 ``` 辰年属申子辰组,小限从戌宫起,戌宫的索引是 8。 *** ## GetBrightness [#getbrightness] **用途** 查某颗星落在某宫时的亮度。 **签名** ```go func GetBrightness(starKey string, palaceIndex int, config *Config) (string, error) ``` **参数** | 参数 | 类型 | 必填 | 默认 | 说明 | | ------------- | --------- | -- | -- | --------------- | | `starKey` | `string` | 是 | — | 星耀标识 | | `palaceIndex` | `int` | 是 | — | 宫位索引,越界会对 12 取模 | | `config` | `*Config` | 是 | — | 自定义亮度表会改变结果 | **返回值** 亮度标识;该星没有亮度表时返回空串。 **示例** ```go a, _ := iztro.GetBrightness(iztro.StarZiweiMaj, 4, nil) b, _ := iztro.GetBrightness(iztro.StarLucunMin, 0, nil) fmt.Printf("%q %q\n", a, b) ``` **输出** ```text "miao" "" ``` 紫微在午宫(索引 4)庙;禄存没有亮度表。 *** ## GetMutagen / GetMutagensByHeavenlyStem [#getmutagen--getmutagensbyheavenlystem] **用途** 查天干四化。 **斗数含义** 十天干各自固定指派四颗星化禄、权、科、忌。 `GetMutagen` 问「这颗星在这个天干下化什么」, `GetMutagensByHeavenlyStem` 问「这个天干化哪四颗星」。 **签名** ```go func GetMutagen(starKey string, stemKey string, config *Config) (string, error) func GetMutagensByHeavenlyStem(stemKey string, config *Config) ([]string, error) ``` **返回值** `GetMutagen` 返回四化标识,该星不在此天干的四化表内时返回空串。 `GetMutagensByHeavenlyStem` 返回四项切片,顺序为**禄、权、科、忌**。 **示例** ```go a, _ := iztro.GetMutagen(iztro.StarTaiyangMaj, iztro.StemGeng, nil) b, _ := iztro.GetMutagen(iztro.StarZiweiMaj, iztro.StemGeng, nil) c, _ := iztro.GetMutagensByHeavenlyStem(iztro.StemGeng, nil) fmt.Printf("%q %q\n%v\n", a, b, c) ``` **输出** ```text "sihuaLu" "" [taiyangMaj wuquMaj taiyinMaj tiantongMaj] ``` *** ## GetSoulAndBody [#getsoulandbody] **用途** 由农历月索引、时辰与年干推命宫、身宫。 **斗数含义** 命宫是整张盘的起点:从寅宫起正月,顺数到生月,再从生月逆数到生时。 身宫用同样的起点但顺数生时。命宫的天干由五虎遁从年干推得。 **签名** ```go func GetSoulAndBody(monthIndex int, timeIndex uint8, yearlyStemKey string) (*SoulAndBody, error) ``` **参数** | 参数 | 类型 | 必填 | 默认 | 说明 | | --------------- | -------- | -- | -- | ------------------------------------- | | `monthIndex` | `int` | 是 | — | 农历月索引,正月为 0;由 `FixLunarMonthIndex` 求得 | | `timeIndex` | `uint8` | 是 | — | 时辰索引 0–12 | | `yearlyStemKey` | `string` | 是 | — | 生年天干标识 | **返回值** `*SoulAndBody`,含 `SoulIndex`、`BodyIndex`、`HeavenlyStemOfSoul`、`EarthlyBranchOfSoul`。 **示例** ```go sb, _ := iztro.GetSoulAndBody(6, 2, iztro.StemGeng) fmt.Printf("%+v\n", *sb) ``` **输出** ```text {SoulIndex:4 BodyIndex:8 HeavenlyStemOfSoul:renHeavenly EarthlyBranchOfSoul:wuEarthly} ``` *** ## GetFiveElementsClass [#getfiveelementsclass] **用途** 由命宫干支推五行局。 **斗数含义** 五行局(水二、木三、金四、土五、火六)决定两件大事: 紫微星的起宫位置,以及大限的起运岁数。 **签名** ```go func GetFiveElementsClass(stemKey string, branchKey string) (string, error) ``` **返回值** 五行局标识。 **示例** ```go fe, _ := iztro.GetFiveElementsClass(iztro.StemRen, iztro.BranchWu) fmt.Println(fe) ``` **输出** ```text wood3rd ``` *** ## GetPalaceNames [#getpalacenames] **用途** 由命宫索引推十二宫名。 **斗数含义** 命宫定下后,其余十一宫按固定顺序逆时针排开: 命、兄弟、夫妻、子女、财帛、疾厄、迁移、仆役、官禄、田宅、福德、父母。 **签名** ```go func GetPalaceNames(soulIndex int) ([]string, error) ``` **返回值** 十二项**标识**切片(不是译名),**按宫位索引排列**—— 第 `i` 项就是 `chart.Palaces[i]` 的 `NameKey`。 与 `GetConstants().Palaces` 不同:那个给的是宫名的固定排列顺序,与具体盘无关。 **示例** ```go names, _ := iztro.GetPalaceNames(4) fmt.Println(names[:4]) ``` **输出** ```text [wealthPalace childrenPalace spousePalace siblingsPalace] ``` 命宫在索引 4,因此索引 0(寅宫)是财帛。 *** ## GetDecadalsAndAges [#getdecadalsandages] **用途** 由命宫索引与五行局推十二宫的大限与小限。 **斗数含义** 大限起运岁数由五行局决定(水二局 2 岁起、木三局 3 岁起,依此类推), 顺逆由性别阴阳与年支阴阳决定;小限起宫由年支决定,按虚岁逐年推移。 **签名** ```go func GetDecadalsAndAges( soulIndex int, fiveElementsClass string, gender Gender, yearlyStemKey, yearlyBranchKey string, ) (DecadalsAndAges, error) ``` **参数** | 参数 | 类型 | 必填 | 默认 | 说明 | | ------------------- | -------- | -- | -- | ----------------------------- | | `soulIndex` | `int` | 是 | — | 命宫宫位索引 | | `fiveElementsClass` | `string` | 是 | — | 五行局标识 | | `gender` | `Gender` | 是 | — | `GenderMale` 或 `GenderFemale` | | `yearlyStemKey` | `string` | 是 | — | 年干标识 | | `yearlyBranchKey` | `string` | 是 | — | 年支标识 | **返回值** `DecadalsAndAges`,含 `Decadals []Decadal` 与 `Ages [][]int`,均按宫位索引排列。 `Decadal` 与宫位上的 `palace.Decadal` 是同一个类型: | 字段 | 类型 | 说明 | | ------------------------------------ | -------- | ------------ | | `Range` | `[2]int` | 大限起止虚岁,含两端 | | `HeavenlyStem` / `HeavenlyStemKey` | `string` | 大限天干的译名 / 标识 | | `EarthlyBranch` / `EarthlyBranchKey` | `string` | 大限地支的译名 / 标识 | **示例** ```go da, _ := iztro.GetDecadalsAndAges(4, "wood3rd", iztro.GenderFemale, iztro.StemGeng, iztro.BranchChen) fmt.Printf("%+v\n", da.Decadals[0]) fmt.Println(da.Ages[0][:3]) ``` **输出** ```text {Range:[43 52] HeavenlyStem:戊 HeavenlyStemKey:wuHeavenly EarthlyBranch:寅 EarthlyBranchKey:yinEarthly} [9 21 33] ``` **边界与陷阱** 整盘排出的每个宫位上已有 `Decadal` 与 `Ages` 字段,内容与本函数一致, 连译名与标识两组字段的含义都相同。这个函数用于不排整盘、只推大限小限的场合。 这个函数不收 `language` 参数,`Decadal` 的译名字段一律是中文。 要别的语言用 `HeavenlyStemKey` 走 [`Translate`](/zh/docs/go/i18n#translate)。 *** ## FixLunarMonthIndex / FixLunarDayIndex [#fixlunarmonthindex--fixlunardayindex] **用途** 求修正后的农历月索引与日索引。 **斗数含义** 闰月归属与晚子时归属是斗数两个长期有争议的边界,这两个函数把规则落定: 闰月十六日起按下月算(可关,且晚子时不进位),晚子时的日索引属次日。 **签名** ```go func FixLunarMonthIndex(lunarMonth int, lunarDay int, isLeap bool, timeIndex uint8, fixLeap bool) (int, error) func FixLunarDayIndex(lunarDay int, timeIndex uint8) (int, error) ``` **返回值** 月索引为 0-based(正月为 0);日索引在晚子时不减一。 `FixLunarMonthIndex` 进位要同时满足四个条件:`isLeap` 为真、`fixLeap` 为真、 `lunarDay` 大于 15、且 `timeIndex` 不是 12。四者缺一,就按本月算。 **示例** ```go m, _ := iztro.FixLunarMonthIndex(7, 17, false, 2, true) d1, _ := iztro.FixLunarDayIndex(17, 2) d2, _ := iztro.FixLunarDayIndex(17, 12) fmt.Println(m, d1, d2) ``` **输出** ```text 6 16 17 ``` 七月非闰月,索引为 6;十七日在寅时减一得 16,在晚子时属次日故保持 17。 *** ## TranslateChineseDate [#translatechinesedate] **用途** 把四柱干支拼成展示串。 **签名** ```go func TranslateChineseDate(pillars [4][2]string, language Language) (string, error) ``` **参数** | 参数 | 类型 | 必填 | 默认 | 说明 | | ---------- | -------------- | -- | -- | -------------------------------- | | `pillars` | `[4][2]string` | 是 | — | 四柱标识 \[年, 月, 日, 时],每柱为 \[天干, 地支] | | `language` | `Language` | 是 | — | 盘面语言 | **返回值** 词条均为单字符时柱内紧凑相连、柱间空格; 任一词条为多字符时柱内空格、柱间 `-`。 **示例** ```go s, _ := iztro.TranslateChineseDate([4][2]string{ {iztro.StemGeng, iztro.BranchChen}, {iztro.StemJia, iztro.BranchShen}, {iztro.StemBing, iztro.BranchWu}, {iztro.StemGeng, iztro.BranchYin}, }, "zh-CN") fmt.Println(s) // 星盘上的四柱标识可直接取 s2, _ := iztro.TranslateChineseDate(chart.RawDates.ChineseDate.PillarKeys(), iztro.LanguageZhCN) fmt.Println(s2) ``` **输出** ```text 庚辰 甲申 丙午 庚寅 庚辰 甲申 丙午 庚寅 ``` **边界与陷阱** 干支标识非法时返回错误。定长数组保证了柱数必为四,不必再校验长度。 *** ## MergeStars [#mergestars] **用途** 把多组「十二宫星耀」按宫位合并成一组。 **斗数含义** 安星是分批进行的:主星、辅星、杂耀各出一组十二宫列表。 要把它们并成一张完整盘面时用这个函数。 **签名** ```go func MergeStars(groups ...[][]Star) ([][]Star, error) ``` **参数** | 参数 | 类型 | 必填 | 默认 | 说明 | | -------- | ------------- | -- | -- | ------------------ | | `groups` | `...[][]Star` | 是 | — | 若干组十二宫星耀,每组长度须为 12 | **返回值** 合并后的十二宫切片,同宫内按传入顺序首尾相接。 **示例** ```go birth := iztro.StarBirth{SolarDate: "2000-8-16", TimeIndex: 2, Gender: iztro.GenderFemale, FixLeap: true} major, _ := iztro.GetMajorStar(birth) minor, _ := iztro.GetMinorStar(birth) merged, _ := iztro.MergeStars(major, minor) names := []string{} for _, s := range merged[0] { names = append(names, s.Name) } fmt.Println(names) ``` **输出** ```text [武曲 天相 天马] ``` **边界与陷阱** 某一组的长度不是 12 时返回错误。这是纯本地实现,不经 wasm。 # 安星模块 (/zh/docs/go/star) 按出生数据取某一组星耀的落宫。 不排整盘、只想知道「禄存落在哪一宫」或「这张盘的杂耀怎么分布」时用这一层。 所有索引都是**宫位索引**:0 为寅宫,11 为丑宫。 ## StarBirth [#starbirth] 按出生数据安星的入口共用这一个参数结构。 ```go type StarBirth struct { SolarDate string TimeIndex uint8 Gender string FixLeap bool Language string Config *Config FromStem string FromBranch string } ``` | 字段 | 类型 | 说明 | | ------------------------- | --------- | --------------------- | | `SolarDate` | `string` | 公历日期,格式 `YYYY-M-D` | | `TimeIndex` | `uint8` | 时辰索引 0–12 | | `Gender` | `string` | 性别,决定长生与博士十二神的顺逆 | | `FixLeap` | `bool` | 是否修正闰月 | | `Language` | `string` | 星耀名称的输出语言,留空取 `zh-CN` | | `Config` | `*Config` | 排盘配置,`nil` 取默认 | | `FromStem` / `FromBranch` | `string` | 起五行局的干支;两者须同时给出 | ```go birth := iztro.StarBirth{ SolarDate: "2000-8-16", TimeIndex: 2, Gender: iztro.GenderFemale, FixLeap: true, } ``` 本页示例统一用默认的 `zh-CN`(`Language` 留空即取它),因此星名输出都是中文。 两者同时给出后,五行局改由该干支推算,进而改变紫微天府落点与长生十二神。 其余各组星的起法不受影响。用它可以取到中州派地盘、人盘的安星结果。 *** ## GetStartIndex [#getstartindex] **用途** 求紫微、天府的起始宫位。 **斗数含义** 紫微是全盘的锚点:由五行局与农历生日按「起紫微星诀」定位, 其余十三颗主星再依紫微与天府的位置铺开。天府与紫微的位置互为镜像。 **签名** ```go func GetStartIndex(birth StarBirth) (StartIndex, error) ``` **返回值** `StartIndex{ ZiweiIndex, TianfuIndex int }`。 **示例** ```go s, _ := iztro.GetStartIndex(birth) fmt.Printf("%+v\n", s) ``` **输出** ```text {ZiweiIndex:4 TianfuIndex:8} ``` *** ## 各组落宫索引 [#各组落宫索引] 以下六个入口形状一致:收 `StarBirth`,返回一个字段全是宫位索引的结构体。 | 函数 | 返回类型 | 字段 | 起法依据 | | --------------------- | ------------------ | ------------------------------------------ | ----------------- | | `GetLuYangTuoMaIndex` | `LuYangTuoMaIndex` | `LuIndex` `YangIndex` `TuoIndex` `MaIndex` | 年干定禄存,禄前羊后陀;天马按年支 | | `GetKuiYueIndex` | `KuiYueIndex` | `KuiIndex` `YueIndex` | 年干 | | `GetChangQuIndex` | `ChangQuIndex` | `ChangIndex` `QuIndex` | 时支 | | `GetKongJieIndex` | `KongJieIndex` | `KongIndex` `JieIndex` | 时支 | | `GetTimelyStarIndex` | `TimelyStarIndex` | `TaifuIndex` `FenggaoIndex` | 时支 | | `GetLuanXiIndex` | `LuanXiIndex` | `HongluanIndex` `TianxiIndex` | 年支 | **示例** ```go l, _ := iztro.GetLuYangTuoMaIndex(birth) c, _ := iztro.GetChangQuIndex(birth) lx, _ := iztro.GetLuanXiIndex(birth) fmt.Printf("%+v\n%+v %+v\n", l, c, lx) ``` **输出** ```text {LuIndex:6 YangIndex:7 TuoIndex:5 MaIndex:0} {ChangIndex:6 QuIndex:4} {HongluanIndex:9 TianxiIndex:3} ``` 擎羊在禄存前一格、陀罗在后一格,这是「禄前羊刃当,禄后陀罗府」的直接体现。 *** ## GetDailyStarIndex / GetMonthlyStarIndex / GetYearlyStarIndex [#getdailystarindex--getmonthlystarindex--getyearlystarindex] **用途** 取按日、按月、按年起的杂耀落宫。 **斗数含义** 杂耀按起法分组:日系星从辅星位置起初一顺数到生日; 月系星按农历月份定位;年系星最多,按年干或年支起。 **返回值** | 函数 | 返回类型 | 字段 | | --------------------- | ------------------ | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | `GetDailyStarIndex` | `DailyStarIndex` | `SantaiIndex` `BazuoIndex` `EnguangIndex` `TianguiIndex` | | `GetMonthlyStarIndex` | `MonthlyStarIndex` | `YuejieIndex` `TianyaoIndex` `TianxingIndex` `YinshaIndex` `TianyueIndex` `TianwuIndex` | | `GetYearlyStarIndex` | `YearlyStarIndex` | 27 项:`XianchiIndex` `HuagaiIndex` `GuchenIndex` `GuasuIndex` `TiancaiIndex` `TianshouIndex` `TianchuIndex` `PosuiIndex` `FeilianIndex` `LongchiIndex` `FenggeIndex` `TiankuIndex` `TianxuIndex` `TianguanIndex` `TianfuIndex` `TiandeIndex` `YuedeIndex` `TiankongIndex` `JieluIndex` `KongwangIndex` `XunkongIndex` `TianshangIndex` `TianshiIndex` `JiekongIndex` `JieshaAdjIndex` `NianjieIndex` `DahaoAdjIndex` | **示例** ```go d, _ := iztro.GetDailyStarIndex(birth) m, _ := iztro.GetMonthlyStarIndex(birth) fmt.Printf("%+v\n%+v\n", d, m) ``` **输出** ```text {SantaiIndex:0 BazuoIndex:10 EnguangIndex:9 TianguiIndex:7} {YuejieIndex:0 TianyaoIndex:5 TianxingIndex:1 YinshaIndex:0 TianyueIndex:9 TianwuIndex:0} ``` **边界与陷阱** 年系杂耀属流年神煞,取年支时用的是 `HoroscopeDivide` 而非 `YearDivide`。 两个配置不同时,年系星与主星、辅星可能基于不同的年支——这是刻意的流派区分。 它们也属年系,但由 `GetLuanXiIndex` 单独给出。 这三项只在 `Algorithm` 为中州派时进入盘面,替换掉截路、空亡与大耗的默认取法; 默认派别下它们仍会被算出来,只是不安进宫位。 *** ## GetMajorStar / GetMinorStar / GetAdjectiveStar [#getmajorstar--getminorstar--getadjectivestar] **用途** 取主星、辅星、杂耀在十二宫的完整分布。 **签名** ```go func GetMajorStar(birth StarBirth) ([][]Star, error) func GetMinorStar(birth StarBirth) ([][]Star, error) func GetAdjectiveStar(birth StarBirth) ([][]Star, error) ``` **返回值** 十二项切片,按宫位索引排列。每项是该宫的 `Star` 切片(可能为空)。 **示例** ```go major, _ := iztro.GetMajorStar(birth) for i := 0; i < 5; i++ { names := []string{} for _, s := range major[i] { names = append(names, s.Name) } fmt.Println(i, names) } ``` **输出** ```text 0 [武曲 天相] 1 [太阳 天梁] 2 [七杀] 3 [天机] 4 [紫微] ``` **边界与陷阱** 返回的 `Star` 带亮度与生年四化标记,与整盘排出的完全一致—— 它们走的是同一段代码。要取整盘的话直接用 `BySolar` 更省事。 *** ## GetChangsheng12 / GetBoShi12 / GetYearly12 [#getchangsheng12--getboshi12--getyearly12] **用途** 取四组十二神在十二宫的排列。 **斗数含义** 这四组各是十二个标记排满十二宫,每宫恰好一个: 长生十二神按五行局起、随性别与年支阴阳定顺逆; 博士十二神从禄存起、同样定顺逆; 岁前十二神从年支起顺行;将前十二神按年支三合组起。 **签名** ```go func GetChangsheng12(birth StarBirth) ([]string, error) func GetBoShi12(birth StarBirth) ([]string, error) func GetYearly12(birth StarBirth) (Yearly12, error) ``` **返回值** 十二项标识切片,按宫位索引排列。 `GetYearly12` 返回 `Yearly12{ Suiqian12, Jiangqian12 []string }`。 **示例** ```go cs, _ := iztro.GetChangsheng12(birth) bs, _ := iztro.GetBoShi12(birth) y, _ := iztro.GetYearly12(birth) fmt.Println(cs[:4]) fmt.Println(bs[:4]) fmt.Println(y.Suiqian12[:4]) fmt.Println(y.Jiangqian12[:4]) ``` **输出** ```text [jue mu si bing] [faylian zhoushu jiangjun xiaohao] [diaoke bingfu suijian huiqi] [suiyi xiishen huagai jiesha] ``` 返回的是标识而非译名,要展示用 `Translate(key, language)`。 *** ## GetChangsheng12StartIndex / GetJiangqian12StartIndex [#getchangsheng12startindex--getjiangqian12startindex] **用途** 只取两组十二神的起始宫位,不排整组。 **斗数含义** 长生起点由五行局定:水二局长生在申、木三局在亥、金四局在巳、 土五局在申、火六局在寅。将星起点由年支三合组定:寅午戌年在午、申子辰年在子、 巳酉丑年在酉、亥卯未年在卯。 **签名** ```go func GetChangsheng12StartIndex(fiveElementsClass string) (int, error) func GetJiangqian12StartIndex(branchKey string) (int, error) ``` **返回值** `int`,0–11。这两个函数不需要出生数据。 **示例** ```go a, _ := iztro.GetChangsheng12StartIndex(iztro.ClassWater2nd) b, _ := iztro.GetChangsheng12StartIndex(iztro.ClassFire6th) c, _ := iztro.GetJiangqian12StartIndex(iztro.BranchZi) d, _ := iztro.GetJiangqian12StartIndex(iztro.BranchWu) fmt.Println(a, b, c, d) ``` **输出** ```text 6 0 10 4 ``` 水二局长生在申(索引 6),火六局在寅(索引 0)。 *** ## GetHoroscopeStar [#gethoroscopestar] **用途** 取某个运限层级的流耀分布。 **斗数含义** 流耀是随运限产生的十颗星:魁钺昌曲禄羊陀马鸾喜。 它们的落宫由该层级的干支决定,名字随层级变化。流年层级额外多一颗年解。 **签名** ```go func GetHoroscopeStar(stemKey, branchKey, scope string, language Language) ([][]Star, error) ``` **参数** | 参数 | 类型 | 必填 | 默认 | 说明 | | ----------- | ---------- | -- | -- | --------- | | `stemKey` | `string` | 是 | — | 该层级的天干标识 | | `branchKey` | `string` | 是 | — | 该层级的地支标识 | | `scope` | `string` | 是 | — | 运限层级,决定星名 | | `language` | `Language` | 是 | — | 盘面语言 | **返回值** 十二项切片,按宫位索引排列。 **各层级的星名对照** | 本命 | 大限 | 流年 | 流月 | 流日 | 流时 | | -- | -- | -- | -- | -- | -- | | 天魁 | 运魁 | 流魁 | 月魁 | 日魁 | 时魁 | | 天钺 | 运钺 | 流钺 | 月钺 | 日钺 | 时钺 | | 文昌 | 运昌 | 流昌 | 月昌 | 日昌 | 时昌 | | 文曲 | 运曲 | 流曲 | 月曲 | 日曲 | 时曲 | | 禄存 | 运禄 | 流禄 | 月禄 | 日禄 | 时禄 | | 擎羊 | 运羊 | 流羊 | 月羊 | 日羊 | 时羊 | | 陀罗 | 运陀 | 流陀 | 月陀 | 日陀 | 时陀 | | 天马 | 运马 | 流马 | 月马 | 日马 | 时马 | | 红鸾 | 运鸾 | 流鸾 | 月鸾 | 日鸾 | 时鸾 | | 天喜 | 运喜 | 流喜 | 月喜 | 日喜 | 时喜 | 标识形如 `yunlu`(运禄)、`liulu`(流禄)、`yuelu`(月禄)、`rilu`(日禄)、`shilu`(时禄)。 **示例** ```go decadal, _ := iztro.GetHoroscopeStar(iztro.StemJia, iztro.BranchZi, iztro.ScopeDecadal, iztro.LanguageZhCN) for i := 0; i < 4; i++ { names := []string{} for _, s := range decadal[i] { names = append(names, s.Name) } fmt.Println(i, names) } ``` **输出** ```text 0 [运禄 运马] 1 [运羊 运鸾] 2 [] 3 [运昌] ``` **边界与陷阱** `ScopeYearly` 的结果里额外含年解,按流年地支定位,安放在十颗流耀之前。 其余层级没有这一颗。 *** ## 低层落宫 [#低层落宫] 上面的函数都从出生数据起算,内部先推出年干支、命宫、修正后的农历月,再落宫。 这一组则直接收那些中间量,自建流程时可以复用。 | 函数 | 收 | 出(字段全为宫位索引 `int`) | | ---------------------------------------------------------------- | ------------ | ----------------------------------------------------- | | `GetZuoYouIndex(lunarMonth)` | 修正后的农历月 1–12 | `ZuoYouIndex{ZuoIndex, YouIndex}` | | `GetHuoLingIndex(branchKey, timeIndex)` | 年支、时辰 | `HuoLingIndex{HuoIndex, LingIndex}` | | `GetHuagaiXianchiIndex(branchKey)` | 年支 | `HuagaiXianchiIndex{HuagaiIndex, XianchiIndex}` | | `GetGuGuaIndex(branchKey)` | 年支 | `GuGuaIndex{GuchenIndex, GuasuIndex}` | | `GetJieshaAdjIndex(branchKey)` | 年支 | `int`,劫煞宫位索引 | | `GetDahaoIndex(branchKey)` | 年支 | `int`,大耗宫位索引 | | `GetNianjieIndex(branchKey)` | 年支 | `int`,年解宫位索引 | | `GetTianshiTianshangIndex(gender, branchKey, soulIndex, config)` | 性别、年支、命宫索引 | `TianshiTianshangIndex{TianshangIndex, TianshiIndex}` | | `GetChangQuIndexByHeavenlyStem(stemKey)` | 天干 | `ChangQuIndex{ChangIndex, QuIndex}` | **示例** ```go chart, _ := iztro.BySolar("2000-8-16", 2, iztro.GenderFemale, true, iztro.LanguageZhCN, nil) yearBranch := chart.RawDates.ChineseDate.YearlyKeys[1] hl, _ := iztro.GetHuoLingIndex(yearBranch, 2) gg, _ := iztro.GetGuGuaIndex(yearBranch) cq, _ := iztro.GetChangQuIndexByHeavenlyStem(iztro.StemJia) fmt.Println(hl, gg, cq) ``` **输出** ```text {2 10} {3 11} {3 7} ``` **边界与陷阱** `GetZuoYouIndex` 收的是修正闰月之后的月份,即 `FixLunarMonthIndex(...) + 1`, 不是农历原始月份。闰月盘直接传原始月份会落错宫。 `GetTianshiTianshangIndex` 的结果随 `config.Algorithm` 变:中州派在阴男阳女 (生年地支阴阳与性别阴阳不同)时天伤天使对调,通行派不对调;`config` 传 `nil` 取默认。 `GetChangQuIndexByHeavenlyStem` 按天干起昌曲,用于运限层级的流昌流曲; 本命盘的文昌文曲按时支走 `GetChangQuIndex`。 # 数据表 (/zh/docs/go/data) 星耀基础信息、天干地支信息、顺序常量与全部标识常量。 排盘算法的输入表与语言无关标识常量。 *** ## StarsInfo [#starsinfo] **用途** 取星耀基础信息表。 **签名** ```go func StarsInfo() (map[string]StarInfo, error) ``` **返回值** 星耀标识 → `StarInfo`。只有二十颗星有记录: **十四主星**加文昌、文曲、火星、铃星、擎羊、陀罗。 | 字段 | 类型 | 说明 | | -------------- | ---------- | -------------------------- | | `Brightness` | `[]string` | 十二宫亮度标识,索引 0 为寅宫;该宫无亮度则为空串 | | `FiveElements` | `string` | 五行;原表未填时为空串 | | `YinYang` | `string` | 阴阳;原表未填时为空串 | **示例** ```go info, _ := iztro.StarsInfo() fmt.Println(len(info)) fmt.Printf("%+v\n", info[iztro.StarZiweiMaj]) fmt.Printf("%q\n", info[iztro.StarTaiyangMaj].FiveElements) ``` **输出** ```text 20 {Brightness:[wang wang de wang miao miao wang wang de wang ping miao] FiveElements:土 YinYang:阴} "" ``` **边界与陷阱** 表中部分星耀的五行或阴阳未填:太阳与七杀两项皆为空串, 贪狼、天相、天梁、破军的阴阳为空串,六颗辅星两项皆为空串。 *** ## HeavenlyStems [#heavenlystems] **用途** 取天干信息表。 **斗数含义** 天干的四化表是四化系统的根:生年干决定生年四化, 宫干决定该宫飞出的四化,运限干决定该层级的四化。 **签名** ```go func HeavenlyStems() (map[string]HeavenlyStemInfo, error) ``` **返回值** 天干标识 → `HeavenlyStemInfo`: | 字段 | 类型 | 说明 | | -------------- | ---------- | ----------------- | | `YinYang` | `string` | 阴阳 | | `FiveElements` | `string` | 五行 | | `Crash` | `string` | 对冲天干标识;戊、己无对冲,为空串 | | `Mutagen` | `[]string` | 四化四星标识,顺序为禄、权、科、忌 | **示例** ```go stems, _ := iztro.HeavenlyStems() fmt.Printf("%+v\n", stems[iztro.StemJia]) fmt.Printf("戊干对冲: %q\n", stems[iztro.StemWu].Crash) ``` **输出** ```text {YinYang:阳 FiveElements:木 Crash:gengHeavenly Mutagen:[lianzhenMaj pojunMaj wuquMaj taiyangMaj]} 戊干对冲: "" ``` *** ## EarthlyBranches [#earthlybranches] **用途** 取地支信息表。 **签名** ```go func EarthlyBranches() (map[string]EarthlyBranchInfo, error) ``` **返回值** 地支标识 → `EarthlyBranchInfo`: | 字段 | 类型 | 说明 | | -------------- | -------- | ---------------- | | `YinYang` | `string` | 阴阳,决定大限与长生十二神的顺逆 | | `FiveElements` | `string` | 五行 | | `Crash` | `string` | 对冲地支标识 | | `Soul` | `string` | 命主星标识(按命宫地支查) | | `Body` | `string` | 身主星标识(按生年地支查) | | `Inside` | `string` | 对应脏腑 | | `Outside` | `string` | 对应身体部位 | | `HealthTip` | `string` | 健康提示 | `Inside` / `Outside` / `HealthTip` 三项只有中文一种写法,不参与国际化。 **示例** ```go branches, _ := iztro.EarthlyBranches() fmt.Printf("%+v\n", branches[iztro.BranchZi]) ``` **输出** ```text {YinYang:阳 FiveElements:水 Crash:wuEarthly Soul:tanlangMaj Body:huoxingMin Inside:胆 Outside:下体 HealthTip:生殖系统、膀胱、尿道之疾病,听觉障碍} ``` *** ## GetConstants [#getconstants] **用途** 取顺序常量与推算规则表。 **签名** ```go func GetConstants() (Constants, error) ``` **返回值** `Constants`: | 字段 | 类型 | 说明 | | ------------------- | ------------------- | ---------------------------------------------------- | | `Languages` | `[]string` | 支持的语言代码 | | `HeavenlyStems` | `[]string` | 天干顺序 | | `EarthlyBranches` | `[]string` | 地支顺序 | | `Zodiac` | `[]string` | 生肖标识,按地支顺序 | | `Signs` | `[]string` | 星座标识,按黄道顺序 | | `Palaces` | `[]string` | 十二宫名,从命宫起**逆时针**排:命、父母、福德、田宅、官禄、仆役、迁移、疾厄、财帛、子女、夫妻、兄弟 | | `Gender` | `map[string]string` | 男女各自的阴阳 | | `ChineseTime` | `[]string` | 时辰标识,早子时起、晚子时止 | | `TimeRange` | `[]string` | 时辰对应的钟点区间 | | `TigerRule` | `map[string]string` | 五虎遁:年干推正月天干 | | `RatRule` | `map[string]string` | 五鼠遁:日干推子时天干 | | `Mutagen` | `[]string` | 四化顺序 | | `FiveElementsClass` | `map[string]int` | 五行局标识 → 局数(水二局 2 …… 火六局 6) | **示例** ```go c, _ := iztro.GetConstants() fmt.Println(c.Languages) fmt.Println(c.Zodiac[:3], c.ChineseTime[12], c.TimeRange[2]) fmt.Println(c.Gender) fmt.Println("甲年正月干:", c.TigerRule[iztro.StemJia]) fmt.Println(c.Palaces) fmt.Println(c.FiveElementsClass[iztro.ClassWood3rd], iztro.FiveElementsClassNumber(iztro.ClassFire6th)) ``` **输出** ```text [en-US ja-JP ko-KR zh-CN zh-TW vi-VN] [rat ox tiger] lateRatHour 03:00~05:00 map[female:阴 male:阳] 甲年正月干: bingHeavenly [soulPalace parentsPalace spiritPalace propertyPalace careerPalace friendsPalace surfacePalace healthPalace wealthPalace childrenPalace spousePalace siblingsPalace] 3 6 ``` **边界与陷阱** `Palaces` 给的是宫名的**排列顺序**,不是某张盘上第 `i` 格叫什么。 要那个用 [`GetPalaceNames(soulIndex)`](/zh/docs/go/util#getpalacenames)。 `FiveElementsClassNumber(key)` 不必先取 `Constants`,未知标识返回 0。 `Languages` 的顺序是 iztro 词表的合并次序(en-US 起),不是常量的声明次序。 [`KeyOf`](/zh/docs/go/i18n#keyof) 的逐语言扫描顺序与它一致。 *** ## 标识常量 [#标识常量] 包里的标识常量取值就是语言无关标识,可直接与数据对象的 `*Key` 字段比较。 | 前缀 | 数量 | 例 | | ------------- | ------ | -------------------------------------------------------------------------------------------------------- | | `Palace*` | 12 + 2 | `PalaceSoul`、`PalaceWealth`、`PalaceBody`、`PalaceOriginal` | | `Star*` | 162 | `StarZiweiMaj`、`StarLucunMin`、`StarYunlu` | | `Stem*` | 10 | `StemJia`、`StemGeng` | | `Branch*` | 12 | `BranchZi`、`BranchWu` | | `Mutagen*` | 4 | `MutagenLu`、`MutagenJi` | | `Brightness*` | 7 | `BrightnessMiao`、`BrightnessWang` | | `Class*` | 5 | `ClassWater2nd`、`ClassWood3rd`、`ClassMetal4th`、`ClassEarth5th`、`ClassFire6th` | | `Scope*` | 6 | `ScopeOrigin`、`ScopeDecadal` | | `StarType*` | 8 | `StarTypeMajor`、`StarTypeTough` | | `Gender*` | 2 | `GenderMale`、`GenderFemale`(类型 `Gender`) | | `Language*` | 6 | `LanguageZhCN`、`LanguageZhTW`、`LanguageEnUS`、`LanguageJaJP`、`LanguageKoKR`、`LanguageViVN`(类型 `Language`) | | `*LeapMonth*` | 3 | `NotLeapMonth`、`LeapMonthKeep`、`LeapMonthFixed`(类型 `LeapMonth`,`ByLunar` 的闰月处理方式) | `Gender`、`Language`、`LeapMonth` 是具名字符串类型:作为入口参数时编译器会挡住把别的字符串误传进来的错误, 字面量(`"male"`、`"zh-CN"`)仍可直接写;`Astrolabe.GenderKey` 与 `Astrolabe.Language` 字段也是这两个类型。 其余常量是无类型的字符串常量,与 `*Key` 字段直接比较。 配置类另有六组: | 前缀 | 取值 | | ------------------ | ------------------------------------------------ | | `YearDivide*` | `YearDivideNormal` / `YearDivideExact` | | `HoroscopeDivide*` | `HoroscopeDivideNormal` / `HoroscopeDivideExact` | | `AgeDivide*` | `AgeDivideNormal` / `AgeDivideBirthday` | | `DayDivide*` | `DayDivideForward` / `DayDivideCurrent` | | `Algorithm*` | `AlgorithmDefault` / `AlgorithmZhongzhou` | | `Astro*` | `AstroHeaven` / `AstroEarth` / `AstroHuman` | 写 `iztro.ClassWood3rd`,不是 `iztro.FiveElementsWood3rd`—— 后者不存在,编译不过。取局数用 `iztro.FiveElementsClassNumber(key)`。 **示例** ```go soul := chart.Palace(iztro.PalaceSoul) fmt.Println(soul.MajorStars[0].Key == iztro.StarZiweiMaj) fmt.Println(iztro.StarZiweiMaj, iztro.MutagenLu, iztro.PalaceWealth) fmt.Println(iztro.ClassWood3rd, iztro.GenderFemale, iztro.LanguageEnUS) // 排盘入口的 gender 与 language 也用这两组常量 en, err := iztro.BySolar("2000-8-16", 2, iztro.GenderFemale, true, iztro.LanguageEnUS, nil) if err != nil { log.Fatal(err) } fmt.Println(en.Palace(iztro.PalaceSoul).MajorStars[0].Name) ``` **输出** ```text true ziweiMaj sihuaLu wealthPalace wood3rd female en-US emperor ``` 它们都是无类型字符串常量,`chart.Palace("soulPalace")` 与 `chart.Palace(iztro.PalaceSoul)` 完全等价。 常量的价值在于 IDE 补全与拼写检查,而非类型约束。 # 翻译 (/zh/docs/go/i18n) 标识与译名的双向查找。 星盘上每个字段都同时给出译名与 `*Key` 标识,通常不必手工翻译。 这两个函数用于手上只有标识(或只有某种语言的译名)、需要换算的场合。 支持六种语言:`zh-CN`、`zh-TW`、`en-US`、`ja-JP`、`ko-KR`、`vi-VN`。 *** ## Translate [#translate] **用途** 把任意标识译成指定语言。 **签名** ```go func Translate(key string, language Language) (string, error) ``` **参数** | 参数 | 类型 | 必填 | 默认 | 说明 | | ---------- | ---------- | -- | -- | ------ | | `key` | `string` | 是 | — | 语言无关标识 | | `language` | `Language` | 是 | — | 目标语言 | 覆盖十二类共 260 个标识: | 类目 | 数量 | 例 | | ----------- | --- | --------------------------------------------------------- | | 星耀 | 162 | `ziweiMaj`、`changsheng`、`yunlu` | | 宫位(含身宫、来因宫) | 14 | `soulPalace`、`wealthPalace`、`bodyPalace`、`originalPalace` | | 天干 | 10 | `jiaHeavenly` | | 地支 | 12 | `ziEarthly` | | 亮度 | 7 | `miao`、`wang` | | 四化 | 4 | `sihuaLu` | | 五行局 | 5 | `water2nd` | | 性别 | 2 | `male`、`female` | | 生肖 | 12 | `rat`、`ox` | | 时辰 | 13 | `earlyRatHour` | | 星座 | 12 | `aries` | | 运限层级 | 7 | `decadal`、`turn` | **返回值** 译名;未知标识返回空串(不是错误)。 **示例** ```go a, _ := iztro.Translate(iztro.StarZiweiMaj, iztro.LanguageEnUS) b, _ := iztro.Translate(iztro.PalaceSoul, iztro.LanguageJaJP) c, _ := iztro.Translate("nosuch", iztro.LanguageZhCN) fmt.Printf("%q %q %q\n", a, b, c) ``` **输出** ```text "emperor" "命宮" "" ``` **边界与陷阱** 标识查不到不算异常,返回空串。要区分「译名恰好是空串」与「标识不存在」时, 先确认标识在上表的类目内。 *** ## KeyOf [#keyof] **用途** 由任意语言的译名反查标识。 **签名** ```go func KeyOf(text string) (string, error) func KeyOfIn(text, keyFilter string) (string, error) ``` **参数** | 参数 | 类型 | 必填 | 默认 | 说明 | | ----------- | -------- | -- | -- | ------------------- | | `text` | `string` | 是 | — | 任一支持语言下的译名 | | `keyFilter` | `string` | 是 | — | 限定标识名须含的子串,用于消歧同形译名 | **返回值** 标识;查不到返回空串。 **示例** ```go a, _ := iztro.KeyOf("紫微") b, _ := iztro.KeyOf("emperor") c, _ := iztro.KeyOf("자미") d, _ := iztro.KeyOf("查无此名") fmt.Printf("%q %q %q %q\n", a, b, c, d) ``` **输出** ```text "ziweiMaj" "ziweiMaj" "ziweiMaj" "" ``` 三种语言的译名都落到同一个标识。 **边界与陷阱** 少数译名在多个类目下同形:en-US 的 `horse` 既是生肖马也是天马, `dragon` 既是生肖龙也是青龙,ko-KR 的 `사` 既是地支巳也是长生12神的死。 `KeyOf` 逐语言、每种语言内逐标识取先命中者,顺序与 iztro 的 `kot` 完全一致 (有金标测试逐例守着)。要指定类目就用 `KeyOfIn`——标识名含该子串才纳入比对: ```go a, _ := iztro.KeyOf("horse") // "horse"(生肖马) b, _ := iztro.KeyOfIn("horse", "Min") // "tianmaMin"(天马) c, _ := iztro.KeyOf("유시") // "hourly"(流时) d, _ := iztro.KeyOfIn("유시", "Hour") // "roosterHour"(酉时) e, _ := iztro.KeyOfIn("horse", "Palace") // "" ``` 常用子串:`Maj` 十四主星、`Min` 辅星、`Heavenly` / `Earthly` 干支、 `Palace` 宫位、`Hour` 时辰。限定后无匹配返回空串,不退回未限定的结果。 `KeyOf` 会遍历 260 个标识 × 6 种语言,且经一次 wasm 往返。 不要放在每宫每星的内层循环里——那种场合直接用数据自带的 `*Key` 字段。 *** ## AllKeys [#allkeys] **用途** 取全部 260 个可翻译标识。 **签名** ```go func AllKeys() ([]string, error) ``` **返回值** 标识切片,顺序即 `KeyOf` 的反查次序: 运限层级、生肖、时辰、星座、五行局、天干、地支、亮度、四化、星耀、宫位、性别, 与 iztro 各语言翻译文件的合并次序一致。 **示例** ```go keys, _ := iztro.AllKeys() first, _ := iztro.Translate(keys[0], iztro.LanguageZhCN) fmt.Println(len(keys), keys[:4], first) ``` **输出** ```text 260 [decadal childhood yearly monthly] 大限 ``` 要遍历某一类目自己的标识时,用 `keys.go` 的常量或 `GetConstants()` 更省事。 *** ## 没有全局语言开关 [#没有全局语言开关] x-iztro 不设「当前语言」这样的全局状态:排盘时语言随参数传入, 翻译函数每次调用都显式指定目标语言。 全局语言开关会让同一段代码在不同调用顺序下产出不同结果, 并发环境尤其危险。显式传参使每次调用的结果只由入参决定。 要在一个进程里同时输出多种语言,直接排多张盘即可,互不干扰: ```go zh, _ := iztro.BySolar("2000-8-16", 2, iztro.GenderFemale, true, iztro.LanguageZhCN, nil) en, _ := iztro.BySolar("2000-8-16", 2, iztro.GenderFemale, true, iztro.LanguageEnUS, nil) fmt.Println(zh.Palace(iztro.PalaceSoul).MajorStars[0].Name, en.Palace(iztro.PalaceSoul).MajorStars[0].Name) ``` **输出** ```text 紫微 emperor ``` 两张盘的 `*Key` 字段完全相同,因此任何基于标识的判断在两张盘上结果一致。 # 扩展星盘 (/zh/docs/go/extend) 用结构体嵌入给星盘补自定义分析方法。 斗数的分析规则千人千面,库不可能穷举。Go 不允许给其他包的类型加方法, 因此扩展点是**嵌入**:把 `*Astrolabe` 嵌进自己的结构体, 新方法与内置方法一样用点号调用,且是编译期检查的。 ## 配方 [#配方] 定义一个结构体,嵌入 `*iztro.Astrolabe` 给这个结构体加方法 用排出的星盘构造它 ```go package main import ( "strings" "github.com/x-haose/x-iztro/go/iztro" ) // MyChart 嵌入星盘,补自己的分析方法。 type MyChart struct { *iztro.Astrolabe } // MajorStar 返回命宫主星名(空宫借对宫),多颗以逗号分隔。 func (c MyChart) MajorStar() string { soul := c.Palace(iztro.PalaceSoul) source := soul if soul.IsEmpty() { source = soul.OppositePalace() } names := make([]string, 0, len(source.MajorStars)) for _, s := range source.MajorStars { if s.Type == iztro.StarTypeMajor { names = append(names, s.Name) } } return strings.Join(names, ",") } // FiveElementsValue 返回五行局的局数。 func (c MyChart) FiveElementsValue() int { return map[string]int{ iztro.ClassWater2nd: 2, iztro.ClassWood3rd: 3, iztro.ClassMetal4th: 4, iztro.ClassEarth5th: 5, iztro.ClassFire6th: 6, }[c.FiveElementsClassKey] } ``` **用法** ```go chart, _ := iztro.BySolar("2000-8-16", 2, iztro.GenderFemale, true, iztro.LanguageZhCN, nil) my := MyChart{chart} fmt.Println(my.MajorStar()) fmt.Println(my.FiveElementsValue()) // 内置字段与方法照常可用 fmt.Println(my.SolarDate) fmt.Println(my.Palace(iztro.PalaceSoul).Name) // 扩展方法随排盘语言输出 enChart, _ := iztro.BySolar("2000-8-16", 2, iztro.GenderFemale, true, iztro.LanguageEnUS, nil) fmt.Println(MyChart{enChart}.MajorStar()) ``` **输出** ```text 紫微 3 2000-8-16 命宫 emperor ``` 写 `*iztro.Astrolabe` 而不是 `Astrolabe *iztro.Astrolabe`—— 前者让内置字段与方法直接提升到外层,`my.SolarDate` 就能取到; 后者每次都得写 `my.Astrolabe.SolarDate`。 *** ## 扩展别的类型 [#扩展别的类型] 同一套写法适用于宫位与星耀: ```go type MyPalace struct { *iztro.Palace } // IsAfflicted 判断本宫是否「煞忌交冲」:坐煞星且带化忌。 func (p MyPalace) IsAfflicted() bool { return p.HasOneOf( iztro.StarQingyangMin, iztro.StarTuoluoMin, iztro.StarHuoxingMin, iztro.StarLingxingMin, iztro.StarDikongMin, iztro.StarDijieMin, ) && p.HasMutagen(iztro.MutagenJi) } ``` ```go for i := range chart.Palaces { p := MyPalace{&chart.Palaces[i]} if p.IsAfflicted() { fmt.Println(p.Name, "煞忌交冲") } } ``` **输出** ```text 疾厄 煞忌交冲 ``` *** ## 用接口约束扩展 [#用接口约束扩展] 要求多种星盘类型提供同一组分析能力时,用接口: ```go type WealthAnalyzer interface { WealthScore() int HasWealthPattern() bool } func report(a WealthAnalyzer) { fmt.Println(a.WealthScore(), a.HasWealthPattern()) } ``` 任何实现了这两个方法的类型都能传进去,编译期检查。 *** ## 组织建议 [#组织建议] `WealthChart`、`CareerChart`、`HealthChart` 各自嵌入星盘, 使用方按需构造。堆成一个大类型会让所有调用点都被迫带上全部方法。 `s.Key == iztro.StarZiweiMaj` 在任何输出语言下都成立; `s.Name == "紫微"` 只在中文盘上成立。展示时才用 `Name`。 扩展方法通常只读,用值接收器即可——嵌入的是指针,复制外层结构体不会复制星盘。 方法要改外层结构体自己的字段时才需要指针接收器。 *** ## 与运行期注入的区别 [#与运行期注入的区别] 嵌入在编译期完成,与运行期往对象上挂函数的做法相比: | | 嵌入 | 运行期注入 | | ------ | -------- | ------- | | 方法是否存在 | 编译期确定 | 运行期才知道 | | 类型检查 | 有 | 无 | | 调用开销 | 与内置方法相同 | 多一次动态查找 | | 出错时机 | 编译失败 | 运行时报错 | | 作用范围 | 只影响自己的类型 | 全局或按实例 | 代价是扩展方法必须在编译期就写好,不能由配置文件或用户输入动态决定。 需要那种灵活度时,用一张 `map[string]func(*iztro.Astrolabe) bool` 自行分派。 # 错误处理 (/zh/docs/go/errors) *Error 的 Code 分类、四个哨兵、触发条件与处理模式。 需要计算的入口都返回 `(值, error)`。日期格式、日期存在性、年份范围、 时辰索引在核心层前置校验;性别、语言、标识、配置这些字符串取值在绑定层校验。 纯查询方法(`Palace`、`Star`、`Has` 等)不返回错误,查不到时返回 `nil` 或零值。 ## \*Error [#error] 包内所有失败都返回同一个具体类型: ```go type Error struct { Code string // 机器可读的类别,Code* 常量之一 Message string // 错误描述原文(英文) } func (e *Error) Error() string // 返回 "iztro: " + Message func (e *Error) Unwrap() error // 返回本类别的哨兵,供 errors.Is 匹配 ``` ### 四个类别 [#四个类别] | 哨兵 | `Code` 常量 | 取值 | 含义 | | --------------------- | ---------------------- | -------------------- | ------------------------------------ | | `ErrInvalidDate` | `CodeInvalidDate` | `invalid_date` | 日期格式非法、日期不存在,或超出公历 1583–9999 | | `ErrInvalidTimeIndex` | `CodeInvalidTimeIndex` | `invalid_time_index` | 时辰索引越界(合法值 0–12) | | `ErrInvalidArgument` | `CodeInvalidArgument` | `invalid_argument` | 其余入参或配置非法:性别、语言、星耀标识、自定义表 | | `ErrInternal` | `CodeInternal` | `internal` | 库内部缺陷或 Go 侧运行时故障,如 wasm 实例化失败、结果解码失败 | `Code` 的四个取值与 Rust 的 `IztroError::code()`、Python 的 `IztroError.code` 是同一套,跨语言分支逻辑可以照抄。 ### 两种判断姿势 [#两种判断姿势] ```go _, err := iztro.BySolar("2000-2-30", 2, iztro.GenderFemale, true, iztro.LanguageZhCN, nil) // 按类别分支:errors.Is 对哨兵匹配 fmt.Println(errors.Is(err, iztro.ErrInvalidDate)) fmt.Println(errors.Is(err, iztro.ErrInvalidTimeIndex)) // 要拿 Code 与原文:errors.As 取出具体类型 var e *iztro.Error if errors.As(err, &e) { fmt.Println(e.Code, "|", e.Message) } fmt.Println(err) ``` **输出** ```text true false invalid_date | invalid solar date '2000-2-30': day is out of range for that month iztro: invalid solar date '2000-2-30': day is out of range for that month ``` `Error()` 在 `Message` 前补 `iztro: `,与调用方自己的错误容易区分; `Message` 字段本身不带前缀。消息主体来自核心层,三种语言绑定完全一致, 但**文案会随版本调整**——要在程序里分支请用 `errors.Is` 或 `Code`,别解析文案。 *** ## 日期相关 [#日期相关] | 情形 | 例 | 消息主体 | | --------------- | ------------- | ------------------------------------ | | 格式不是 `YYYY-M-D` | `"2000/8/16"` | `expected 'YYYY-M-D'` | | 年、月、日不是数字 | `"abc-8-16"` | `year is not a number` | | 月份越界 | `"2000-13-1"` | `month must be within 1-12` | | 该月没有这一天 | `"2000-2-30"` | `day is out of range for that month` | | 年份超出支持范围 | `"1500-1-1"` | `year must be within 1583-9999` | `Code` 为 `invalid_date`。农历专有(只有 `ByLunar` 与两个 `*ByLunarDate` 查询会碰到): | 情形 | 例 | 消息主体 | | --------- | -------------------- | ------------------------------------------ | | 该农历年没有这个月 | 表内缺月 | `month does not exist in that lunar year` | | 该农历月没有这一天 | `"2000-7-30"`(七月是小月) | `day is out of range for that lunar month` | 农历消息的前缀是 `invalid lunar date '<原串>': `,公历是 `invalid solar date '<原串>': `, 从消息就能看出走的是哪个入口。 **示例** ```go for _, date := range []string{"2000-13-1", "2000-2-30", "1500-1-1"} { if _, err := iztro.BySolar(date, 2, iztro.GenderFemale, true, iztro.LanguageZhCN, nil); err != nil { fmt.Println(err) } } ``` **输出** ```text iztro: invalid solar date '2000-13-1': month must be within 1-12 iztro: invalid solar date '2000-2-30': day is out of range for that month iztro: invalid solar date '1500-1-1': year must be within 1583-9999 ``` 消息里带上了原始输入,便于在批量处理时定位是哪一条数据出的问题。 **边界与陷阱** 1582 年格里历改革当年有一段不存在的日期。底层历法库在这些日期上没有定义, 因此支持范围从改革完成后的 1583 年起算。上限 9999 是农历数据表的覆盖终点。 `"2000-8-16"` 与 `"2000-08-16"` 都接受。分隔符必须是 `-`。 `ByLunar` 会检查该农历年该月是否真的存在,以及该月有多少天(大月 30、小月 29)。 `leap` 标为闰月但那年那月无闰月时不报错,按普通月处理;`leap` 不是三个 `LeapMonth` 取值之一时返回 `ErrInvalidArgument`。 *** ## 时辰索引 [#时辰索引] **触发条件** 时辰索引大于 12。 **示例** ```go _, err := iztro.BySolar("2000-8-16", 13, iztro.GenderFemale, true, iztro.LanguageZhCN, nil) fmt.Println(err) ``` **输出** ```text iztro: time_index must be 0-12, got 13 ``` **边界与陷阱** 子时跨午夜,拆成早子时(索引 0)与晚子时(索引 12),因此合法值有 13 个。 从小时数换算用 [`TimeToIndex`](/zh/docs/go/util#timetoindex),它保证结果落在合法范围。 *** ## 性别与语言 [#性别与语言] `Code` 均为 `invalid_argument`。 | 参数 | 合法值 | 消息主体 | | ---------- | ------------------------------------------------------- | --------------------------------------------------------------------------------- | | `gender` | `"male"` / `"female"`(`GenderMale` / `GenderFemale` 常量) | `invalid gender 'x': expected 'male' or 'female'` | | `language` | 六个语言代码(`Language*` 常量) | `invalid language 'xx': expected one of zh-CN, zh-TW, en-US, ja-JP, ko-KR, vi-VN` | 语言代码大小写不敏感,连字符与下划线等价(`"zh-cn"`、`"zh_cn"` 都接受)。 `gender` 与 `language` 是具名类型 `Gender` / `Language`,把别的字符串变量误传进来会在编译期被挡下; 字面量拼错则在运行期落到这一类错误。 **示例** ```go _, err := iztro.BySolar("2000-8-16", 2, "x", true, iztro.LanguageZhCN, nil) fmt.Println(err) fmt.Println(errors.Is(err, iztro.ErrInvalidArgument)) ``` **输出** ```text iztro: invalid gender 'x': expected 'male' or 'female' true ``` *** ## 标识相关 [#标识相关] 工具函数与安星函数收的是语言无关标识,未知标识会报错: ```go _, err := iztro.GetBrightness("nosuch", 0, nil) fmt.Println(err) ``` **输出** ```text iztro: unknown star key 'nosuch' ``` 这些函数只认标识不认译名。传 `"紫微"` 会得到 `unknown star key '紫微'`—— 先用 [`KeyOf`](/zh/docs/go/i18n#keyof) 换算。 例外是 `chart.Palace()`、`chart.Star()` 这类**星盘上的查询方法**, 它们同时接受标识与当前排盘语言的译名。 *** ## 配置相关 [#配置相关] `Config` 的六个开关与两张自定义表都在排盘时校验,`Code` 均为 `invalid_argument`: | 情形 | 消息主体 | | ------------ | -------------------------------------------------------------------------------- | | 开关取值未知 | `invalid yearDivide 'nope': expected 'normal' or 'exact'` | | 四化表的天干标识未知 | `invalid mutagens key 'nope': unknown heavenly stem` | | 某个天干的四化不是四项 | `invalid mutagens for 'jiaHeavenly': expected 4 stars (lu, quan, ke, ji), got 1` | | 某颗星的亮度表不是十二项 | `invalid brightness for 'ziweiMaj': expected 12 entries, got 1` | 长度是**严格**校验:四化必须正好四项、亮度必须正好十二项,多一项少一项都报错。 自定义表只收标识不收译名。 *** ## 处理模式 [#处理模式] **批量处理时跳过坏数据** ```go rows := []struct { Date string TimeIndex uint8 Gender iztro.Gender }{ {"2000-8-16", 2, "female"}, {"2000-2-30", 2, "female"}, {"1990-3-3", 13, "male"}, } var charts []*iztro.Astrolabe var failed []string for _, r := range rows { chart, err := iztro.BySolar(r.Date, r.TimeIndex, r.Gender, true, iztro.LanguageZhCN, nil) if err != nil { var e *iztro.Error errors.As(err, &e) failed = append(failed, r.Date+" -> "+e.Code) continue } charts = append(charts, chart) } fmt.Println(len(charts), failed) ``` **输出** ```text 1 [2000-2-30 -> invalid_date 1990-3-3 -> invalid_time_index] ``` **包装成自己的错误** ```go build := func(date string, ti uint8, gender iztro.Gender) (*iztro.Astrolabe, error) { chart, err := iztro.BySolar(date, ti, gender, true, iztro.LanguageZhCN, nil) if err != nil { return nil, fmt.Errorf("排盘失败: %w", err) } return chart, nil } _, err := build("2000-2-30", 2, "female") fmt.Println(err) fmt.Println(errors.Is(err, iztro.ErrInvalidDate)) ``` **输出** ```text 排盘失败: iztro: invalid solar date '2000-2-30': day is out of range for that month true ``` `%w` 保留原错误,调用方可以用 `errors.Is` / `errors.As` 继续判断到具体类别。 *** ## 关于 nil [#关于-nil] 查询方法查不到时返回 `nil` 而非错误——查不到是正常结果,不是异常: ```go p := chart.Palace("nosuchPalace") fmt.Println(p == nil) s, sp := chart.Star("nosuchStar") fmt.Println(s == nil, sp == nil) fmt.Println(chart.Palace(iztro.PalaceSoul).Has("ziweiMj")) ``` **输出** ```text true true true false ``` `chart.Palace(...)`、`chart.Star(...)`、`h.ScopeItem(...)` 都可能返回 `nil`。 直接取字段会 panic。 上面三行都是「拼错了」而不是「盘上没有」:宫名少写一个字母得到 `nil`, 星名少写一个字母让 `Has` 返回 `false`——与真实的「没有这颗星」无法区分。 用包里的 `Palace*` / `Star*` 常量可以让编译器与 IDE 在写错的当场挡下。 名字来自外部输入时,先用 [`KeyOf`](/zh/docs/go/i18n#keyof) 过一遍: 它对无法识别的文本返回空串,可以据此拒绝非法输入。 `chart.Palace("bodyPalace")` 与 `chart.Palace("originalPalace")` 在任何一张盘上都非 `nil`: 来因宫要求宫干与生年干相同且不在子、丑二宫,而寅到酉这十宫刚好把十天干各走一遍, 因此生年干必然命中且只命中一次。真拿到 `nil`,那就是名字拼错了。