# 文档 (/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