# 排盘入口 (/zh/docs/go/astro)

BySolar、ByLunar、Rearranged 与语义化文本投影。



排盘是一切的起点：给出生日期、时辰、性别，得到一个 `*Astrolabe`。

<Callout type="info">
  所有入口都返回 `error`。日期格式与存在性、公历年份范围、时辰索引、性别、
  语言、配置都在核心层前置校验。详见[错误处理](/zh/docs/go/errors)。
</Callout>

***

## BySolar [#bysolar]

**用途**　由公历日期排出本命盘。

**斗数含义**　紫微斗数以农历为算法基础，但绝大多数人只记得公历生日。
本函数先把公历转农历（含年、月、日、时四柱），再据此安星。
换年的时点受 `YearDivide` 影响——正月初一与立春之间出生的人，
两种配置会得到不同的年干支，进而影响四化、命主身主与全部年系星。

**签名**

```go
func BySolar(
    solarDate string,
    timeIndex uint8,
    gender Gender,
    fixLeap bool,
    language Language,
    config *Config,
) (*Astrolabe, error)
```

**参数**

| 参数          | 类型         | 必填 | 默认 | 说明                                                                             |
| ----------- | ---------- | -- | -- | ------------------------------------------------------------------------------ |
| `solarDate` | `string`   | 是  | —  | 公历日期，格式 `YYYY-M-D`，月日不必补零。支持 1583–9999 年                                       |
| `timeIndex` | `uint8`    | 是  | —  | 时辰索引 0–12。0 为早子时（00:00–01:00），12 为晚子时（23:00–24:00）                             |
| `gender`    | `Gender`   | 是  | —  | `GenderMale` 或 `GenderFemale`（字面量 `"male"`/`"female"` 也可）。决定大限顺逆与长生、博士十二神的排列方向 |
| `fixLeap`   | `bool`     | 是  | —  | 是否调整农历闰月。为真时闰月十六日起按下月算（晚子时除外，见下）                                               |
| `language`  | `Language` | 是  | —  | 盘面语言（`LanguageZhCN` 等），影响所有译名字段；`*Key` 标识字段不受影响                                |
| `config`    | `*Config`  | 是  | —  | 排盘配置，传 `nil` 取默认                                                               |

**返回值**　`*Astrolabe`——十二宫、四柱、命主身主、五行局俱全的完整星盘。

**示例**

```go
chart, err := iztro.BySolar("2000-8-16", 2, iztro.GenderFemale, true, iztro.LanguageZhCN, nil)
if err != nil {
    log.Fatal(err)
}

fmt.Println(chart.SolarDate, "|", chart.LunarDate, "|", chart.ChineseDate)
fmt.Println(chart.Sign, chart.Zodiac, chart.FiveElementsClass)
fmt.Println("命主", chart.Soul, "身主", chart.Body)
```

**输出**

```text
2000-8-16 | 二〇〇〇年七月十七 | 庚辰 甲申 丙午 庚寅
狮子座 龙 木三局
命主 破军 身主 文昌
```

**边界与陷阱**

<Accordions>
  <Accordion title="时辰索引为什么是 0–12 而不是 0–11">
    子时横跨午夜，分早子时（00:00–01:00，属当日）与晚子时（23:00–24:00，属次日）。
    两者的日柱不同，紫微起宫也可能差一天，因此必须区分，索引才有 13 个。
    不确定时辰索引时用 `TimeToIndex(hour)` 换算。
  </Accordion>

  <Accordion title="fixLeap 只在闰月生效">
    进位要同时满足四个条件：该农历月确实是闰月、`fixLeap` 为 `true`、
    农历日大于 15、且时辰索引不是 12（晚子时）。四者缺一，月索引就按本月算。
    因此只有农历闰月下半月出生的人，`true` 与 `false` 会得到不同的月索引，
    进而影响左辅右弼与全部月系星。
  </Accordion>

  <Accordion title="config 传 nil 而不是零值">
    `&Config{}` 与 `nil` 效果相同——所有字段都有 `omitempty`，空值不会覆盖默认。
    但显式写 `nil` 更清楚表达「用默认配置」。
  </Accordion>
</Accordions>

***

## ByLunar [#bylunar]

**用途**　由农历日期排出本命盘。

**斗数含义**　农历日期是斗数的原生输入，跳过公历转换这一步。
知道自己农历生日的人直接用它，结果与用对应公历日期调 `BySolar` 完全一致。

**签名**

```go
func ByLunar(
    lunarDate string,
    timeIndex uint8,
    gender Gender,
    leap LeapMonth,
    language Language,
    config *Config,
) (*Astrolabe, error)
```

**参数**　除以下两项外，其余与 `BySolar` 相同；`BySolar` 的 `fixLeap` 在这里并入 `leap`。

| 参数          | 类型          | 必填 | 默认 | 说明                                                                                                                                          |
| ----------- | ----------- | -- | -- | ------------------------------------------------------------------------------------------------------------------------------------------- |
| `lunarDate` | `string`    | 是  | —  | 农历日期，格式 `YYYY-M-D`，月份写正数（闰月由下一参数标记）                                                                                                         |
| `leap`      | `LeapMonth` | 是  | —  | `NotLeapMonth` 非闰月；`LeapMonthKeep` 闰月、按闰月本身排；`LeapMonthFixed` 闰月且十五之后视作次月（iztro `fixLeap`）。标为闰月但那年那月没有闰月时按普通月处理；其它取值返回 `ErrInvalidArgument` |

**返回值**　同 `BySolar`。

**示例**

```go
a, _ := iztro.ByLunar("2000-7-17", 2, iztro.GenderFemale, iztro.NotLeapMonth, iztro.LanguageZhCN, nil)
b, _ := iztro.BySolar("2000-8-16", 2, iztro.GenderFemale, true, iztro.LanguageZhCN, nil)

fmt.Println(a.SolarDate, a.SolarDate == b.SolarDate)
```

**输出**

```text
2000-8-16 true
```

**边界与陷阱**

<Callout type="warn" title="标错闰月的静默失效">
  `leap` 标为闰月但那个月并非闰月时，按普通月排盘，不返回错误（与 iztro 一致）。
  如果需要严格校验，调用前先自行确认该年该月确实有闰月。
</Callout>

***

## Config [#config]

排盘配置。所有字段都可省略，省略即取默认。

```go
type Config struct {
    YearDivide      string
    HoroscopeDivide string
    AgeDivide       string
    DayDivide       string
    Algorithm       string
    AstroType       string
    Mutagens        map[string][]string
    Brightness      map[string][]string
}
```

| 字段                | 取值                                 | 默认          | 说明             |
| ----------------- | ---------------------------------- | ----------- | -------------- |
| `YearDivide`      | `"normal"` / `"exact"`             | `"normal"`  | 年干支按正月初一还是立春换年 |
| `HoroscopeDivide` | `"normal"` / `"exact"`             | `"normal"`  | 流年神煞按哪个分界取年支   |
| `AgeDivide`       | `"normal"` / `"birthday"`          | `"normal"`  | 虚岁按农历年还是生日增长   |
| `DayDivide`       | `"forward"` / `"current"`          | `"forward"` | 晚子时归次日还是当日     |
| `Algorithm`       | `"default"` / `"zhongzhou"`        | `"default"` | 算法派别           |
| `AstroType`       | `"heaven"` / `"earth"` / `"human"` | `"heaven"`  | 排盘视角           |
| `Mutagens`        | 天干标识 → 四星标识                        | —           | 自定义四化表，按天干整表替换 |
| `Brightness`      | 星耀标识 → 十二项亮度标识                     | —           | 自定义亮度表，按星耀整表替换 |

**示例**

```go
cfg := &iztro.Config{
    Algorithm:  iztro.AlgorithmZhongzhou,
    YearDivide: iztro.YearDivideExact,
}
chart, _ := iztro.BySolar("2000-8-16", 2, iztro.GenderFemale, true, iztro.LanguageZhCN, cfg)

fmt.Println(chart.FiveElementsClass)
```

**输出**

```text
木三局
```

每组取值都有对应的常量，不必手写字符串：
`YearDivideNormal` / `YearDivideExact`、`HoroscopeDivideNormal` / `HoroscopeDivideExact`、
`AgeDivideNormal` / `AgeDivideBirthday`、`DayDivideForward` / `DayDivideCurrent`、
`AlgorithmDefault` / `AlgorithmZhongzhou`、`AstroHeaven` / `AstroEarth` / `AstroHuman`。

**边界与陷阱**

<Accordions>
  <Accordion title="自定义表按整表替换，长度严格校验">
    `Mutagens["jiaHeavenly"]` 必须给满四项（禄权科忌），
    `Brightness["ziweiMaj"]` 必须给满十二项，多一项少一项都排盘报错
    （`*Error`，`Code` 为 `invalid_argument`）。未列出的天干与星耀仍用默认表。
    两张表的键与值都只收标识，不收译名。
  </Accordion>

  <Accordion title="自定义表不回显在 chart.Config 上">
    `chart.Config` 由输出 DTO 还原，只含六个开关——两张自定义表是排盘**输入**而非结果，
    不进 DTO（这一点与 JS iztro 的字段契约一致）。

    但星盘内部保留了你传进来的原件，因此 `Rearranged`、`Horoscope`、ToText
    这些二次计算仍然用得上那两张表，不会静默丢失。
    要把配置记录下来，请在自己的调用侧保存 `*Config`。

    ```go
    cfg := &iztro.Config{
        AstroType: iztro.AstroEarth,
        Mutagens: map[string][]string{
            iztro.StemGeng: {iztro.StarTaiyangMaj, iztro.StarWuquMaj,
                iztro.StarTianfuMaj, iztro.StarTiantongMaj},
        },
    }

    chart, err := iztro.BySolar("2000-8-16", 2, iztro.GenderFemale, true, iztro.LanguageZhCN, cfg)
    if err != nil {
        log.Fatal(err)
    }

    fmt.Println(chart.FiveElementsClass, chart.Config.AstroType)
    fmt.Println(chart.Config.Mutagens == nil)
    fmt.Println(chart.Palace(iztro.PalaceSoul).MutagenStarKeys)
    ```

    **输出**

    ```text
    土五局 earth
    true
    [tiantongMaj tianjiMaj wenchangMin lianzhenMaj]
    ```
  </Accordion>

  <Accordion title="&Config{} 与 nil 等价">
    所有字段都带 `omitempty`，空值不进 JSON，因此不会覆盖默认。
    只想改一个开关时，构造一个只填那一项的 `&Config{...}` 即可。
  </Accordion>
</Accordions>

***

## Rearranged [#rearranged]

**用途**　以指定干支为命宫重排本盘，返回新盘；原盘不变。

**斗数含义**　中州派把同一组出生数据看作三张盘：天盘以命宫干支起五行局，
地盘以身宫干支起，人盘以福德宫干支起。起局的干支一变，五行局就变，
紫微天府落点、十二宫名、长生十二神、大限小限随之全部重算。
本方法把这个能力放开到**任意干支**。

**签名**

```go
func (a *Astrolabe) Rearranged(fromStemKey string, fromBranchKey string) (*Astrolabe, error)
```

**参数**

| 参数              | 类型       | 必填 | 默认 | 说明       |
| --------------- | -------- | -- | -- | -------- |
| `fromStemKey`   | `string` | 是  | —  | 新命宫的天干标识 |
| `fromBranchKey` | `string` | 是  | —  | 新命宫的地支标识 |

**返回值**　新的 `*Astrolabe`。重算：命宫身宫、五行局、十四主星、十二宫名、
长生十二神、大限小限、命主星，以及随命宫挪位的天伤、天使、天才。
沿用原盘：辅星、其余杂耀、博士十二神、岁前与将前十二神、身主星。

重排返回的盘上，`Patterns`、运限查询与 ToText 文本投影都按**重排后的布局**
计算——五行局、命宫与大限随重排起点变化；出生数据（日期与四柱）保持不变。

**示例**

```go
chart, _ := iztro.BySolar("2000-8-16", 2, iztro.GenderFemale, true, iztro.LanguageZhCN, nil)

// 从原盘身宫的干支起盘，等价于地盘
var body *iztro.Palace
for i := range chart.Palaces {
    if chart.Palaces[i].IsBodyPalace {
        body = &chart.Palaces[i]
    }
}
earth, _ := chart.Rearranged(body.HeavenlyStemKey, body.EarthlyBranchKey)

fmt.Println("天盘", chart.FiveElementsClass, "→ 地盘", earth.FiveElementsClass)
```

**输出**

```text
天盘 木三局 → 地盘 土五局
```

**边界与陷阱**

<Callout type="info" title="常规三盘不必用这个方法">
  天盘、地盘、人盘用 `&Config{AstroType: iztro.AstroEarth}` 直接排即可，
  两个排盘入口都支持。`Rearranged` 是为「从任意干支起盘」准备的。
</Callout>

<Accordions>
  <Accordion title="身主星不随重排变化">
    身主星按**出生年支**查表，与命宫位置无关，重排不改变出生年。
    命主星按命宫地支查表，因此会跟着更新。
  </Accordion>
</Accordions>

***

## 语义化文本（ToText） [#语义化文本totext]

**用途**　把星盘或运限投影成语义化文本——盘面事实的自然语言形态，
喂给大模型或直接给人读。与 JSON DTO（机器结构）、译文字段（展示）
是同一对象的三种投影。

**签名**

```go
func (a *Astrolabe) ToText() (string, error)
func (h *Horoscope) ToText() (string, error)
func (a *Astrolabe) PalaceToText(target PalaceTarget) (string, error)
func (a *Astrolabe) SurroundedPalacesToText(target PalaceTarget) (string, error)
```

各有 `Context` 变体（`ToTextContext` 等），ctx 用于取消等待 wasm 实例；
各有 `With` 变体（`ToTextWith(opts TextOptions)` 等），带知识包时释义紧跟在对应事实之后，
见 [ToTextWith](/zh/docs/go/astrolabe#totextwith--palacetotextwith--surroundedpalacestotextwith)。
格局文本另见 `PatternsToText`（[格局判定](/zh/docs/go/patterns)）。

**参数**

| 参数       | 类型             | 必填 | 默认 | 说明                                                                                              |
| -------- | -------------- | -- | -- | ----------------------------------------------------------------------------------------------- |
| `target` | `PalaceTarget` | 是  | —  | 宫位寻址：`Key` 非空时按宫名标识定位（`PalaceSoul` 等常量，另接受 `PalaceBody` / `PalaceOriginal`），否则按 `Index`（0–11）取宫 |

**返回值**　`string`——按星盘的排盘语言输出的 Markdown 子集（`#` 标题、`- 标签: 值` 列表、
一张十二宫总览表）；本命文本带格局节与从命宫起的十二宫详解，
运限文本各层带该层视角的四化、流耀与格局行。全部记号见[格式约定](/zh/docs/guide/guides/to-text#格式约定)。

**示例**

```go
chart, _ := iztro.BySolar("2000-8-16", 2, iztro.GenderFemale, true, iztro.LanguageZhCN, nil)
text, _ := chart.ToText()

fmt.Println(strings.Join(strings.Split(text, "\n")[:4], "\n"))
```

**输出**

```text
# 命盘 2000-8-16 寅时 女

## 基本信息
- 阳历: 2000-8-16 · 农历: 二〇〇〇年七月十七 · 时辰: 寅时 (03:00~05:00)
```

**边界与陷阱**

<Callout type="info">
  输出语言跟随星盘的排盘语言，不单独设置。要英文文本就用英文排盘。
</Callout>
