如如居  |   on Monday, January 1, 0001  |  10743   |  22 minutes

iztro 紫微斗数:可复刻实现的算法规格(Implementation Spec)

当前时间:2025-12-19T02:20:40.022Z

目标:仅依据本文,你可以在其它语言中从零复刻本仓库 src/ 里实现的紫微斗数排盘与运限算法(不含命理解读/断语)。

范围:

  • 本命盘(天盘)生成:命/身宫、五行局、十二宫、主星/辅星/杂耀、长生/博士/流年12神、四化、亮度。
  • 中州派扩展:地盘/人盘重排与部分星曜差异。
  • 运限:大限/小限/流年/流月/流日/流时落宫与“流耀”。

源码对应:核心入口在 src/astro/astro.ts,索引/落宫规则集中在 src/star/location.ts

阅读指南(如何用本文复刻)

  • 先实现索引体系:第 3 节的 fixIndex / fixEarthlyBranchIndex 是所有落宫计算的基座,任何一处对不齐都会导致整盘偏移。
  • 复刻本命盘:按 4(命/身宫、五行局、起紫微/天府、安星)→ 5(大限/小限)→ 6(组装 12 宫与元信息)→ 7/8(命主身主、四化亮度) 的顺序实现。
  • 复刻运限:在本命盘可生成后实现第 9 节;它不改变本命盘,只是基于目标日期计算“落在哪一宫”以及对应流耀。

0. 你需要先实现/引入的历法能力

本库依赖 lunar-lite。若你用其它语言复刻,需要能提供等价能力:

  1. 公历 ↔ 农历
  • solar2lunar(solarDate){ lunarYear, lunarMonth, lunarDay, isLeap, ... }
  • lunar2solar(lunarDateStr, isLeapMonth)solarDateStr
  1. 给定公历日期 + 时辰索引,求四柱干支(至少年/月/日/时)
  • getHeavenlyStemAndEarthlyBranchBySolarDate(solarDate, timeIndex, options){ yearly:[stem,branch], monthly:[stem,branch], daily:[stem,branch], hourly:[stem,branch] }

其中 options 至少支持:

  • options.year:年分界(正月初一 or 立春)
  • options.month:月分界(正月初一 or 立春)

注意:本库把“子时”拆成早子与晚子,timeIndex=12 表示 23:00~00:00。


1. 核心常量(实现必须一致)

源码:src/data/constants.ts

1.1 天干/地支枚举(以 key 表示)

HEAVENLY_STEMS = [
  'jiaHeavenly','yiHeavenly','bingHeavenly','dingHeavenly','wuHeavenly',
  'jiHeavenly','gengHeavenly','xinHeavenly','renHeavenly','guiHeavenly'
]

EARTHLY_BRANCHES = [
  'ziEarthly','chouEarthly','yinEarthly','maoEarthly','chenEarthly','siEarthly',
  'wuEarthly','weiEarthly','shenEarthly','youEarthly','xuEarthly','haiEarthly'
]

1.2 十二宫(宫名 key)

PALACES = [
  'soulPalace','parentsPalace','spiritPalace','propertyPalace','careerPalace','friendsPalace',
  'surfacePalace','healthPalace','wealthPalace','childrenPalace','spousePalace','siblingsPalace'
]

1.3 五虎遁(定寅首)

源码:TIGER_RULEsrc/data/constants.ts

TIGER_RULE[yearStem] => stemOfYinMonth

用于:

  • 由“年干”求“寅月天干”(进而推命宫天干、推大限天干等)。

1.4 五行局枚举值

源码:FiveElementsClasssrc/data/constants.ts

water2nd=2, wood3rd=3, metal4th=4, earth5th=5, fire6th=6

2. 全局配置(会改变算法分界/流派)

源码:src/astro/astro.tsconfig() / getConfig()

你在复刻实现里也建议保留同名配置,以保证行为一致。

2.1 配置项

  • yearDivide: 'normal'|'exact'
    • 年干支取法分界:
      • normal:正月初一分界
      • exact:立春分界
  • horoscopeDivide: 'normal'|'exact'
    • 运限(流年/月)取法分界(默认 exact
  • ageDivide: 'normal'|'birthday'
    • 虚岁分界:
      • normal:自然年 +1
      • birthday:过了农历生日才 +1
  • dayDivide: 'current'|'forward'
    • 晚子时(timeIndex=12)算“当天”还是“次日”
  • algorithm: 'default'|'zhongzhou'
    • 安星流派差异开关
  • mutagens?: Map<HeavenlyStemKey, StarKey[4]>
    • 四化表覆盖(顺序固定:禄 权 科 忌)
  • brightness?: Map<StarKey, Brightness[12]>
    • 庙旺落陷表覆盖(按宫位索引 0..11)

3. 统一索引约定(最重要)

3.1 宫位索引 PalaceIndex

本库所有“落宫”计算最终都要落到 0..11,并且定义:

  • 寅宫为 0
  • 之后顺行:卯=1,辰=2,…,丑=11

即:宫位数组 palaces[0] 对应地支“寅”。

3.2 基础工具函数

源码:src/utils/index.ts

3.2.1 fixIndex

说明:fixIndex 是全项目的取模归一函数,用来把任意整数索引归一到固定范围(默认 0..11)。所有“顺数/逆数”落宫最后都必须经过它,避免负数与越界。

function fixIndex(i, mod=12):
  // 返回 [0..mod-1]
  r = i % mod
  if r < 0: r += mod
  return r

3.2.2 地支 → 宫位索引

yinEarthlyEARTHLY_BRANCHES 的 index 为 2。

说明:fixEarthlyBranchIndex 把地支索引转换为“寅宫=0”的宫位索引:地支数组以子起,而本库宫位以寅起,因此需要整体减去寅(=2)的偏移再取模。

function fixEarthlyBranchIndex(earthlyBranchKey):
  return fixIndex(indexOf(EARTHLY_BRANCHES, earthlyBranchKey) - indexOf(EARTHLY_BRANCHES,'yinEarthly'), 12)

等价:palaceIndex = (earthlyIndex - 2) mod 12

3.2.3 小时 → 时辰索引(子时拆分)

源码:timeToIndexsrc/utils/index.ts

说明:timeToIndex 把 24 小时制转换为时辰索引,并把子时拆成早子(0)与晚子(12),以匹配本库所有按时辰落星/算日界的规则。

function timeToIndex(hour):
  if hour == 0: return 0        // 00:00~01:00 早子
  if hour == 23: return 12      // 23:00~00:00 晚子
  return floor((hour + 1)/2)

4. 公历排盘(天盘)主流程:bySolar

这一节描述的是:给定【公历日期 + 时辰 + 性别】如何得到一张完整本命盘。你可以把 bySolar 理解为一个编排器:它先算出命/身宫与五行局,再分别调用“主星/辅星/杂耀/12神/运限”的落宫算法,最后把所有结果按 12 宫索引拼装成 Astrolabe

源码:src/astro/astro.ts bySolar()

4.1 输入/输出

输入:

  • solarDate: 'YYYY-M-D'
  • timeIndex: 0..12
  • gender: male/female 或 男/女(最终只需归一到 male/female)
  • fixLeap: bool(默认 true)

输出:Astrolabe(十二宫 + 元数据)。类型见 src/data/types/astro.ts

4.2 Step 0:晚子时日界处理(dayDivide)

说明:晚子时的“日界”配置。若设为 current,23:00~00:00 的晚子时仍算当天(避免日柱进位);若设为 forward,则保留晚子时语义(后续某些算法会按晚子进位到次日)。

if config.dayDivide == 'current' and timeIndex >= 12:
  tIndex = 0
else:
  tIndex = timeIndex

含义:如果设置“晚子算当天”,则把晚子当作当天子时(不进位到次日)。

4.3 Step 1:取生年干支(yearDivide)

说明:调用历法能力取干支(四柱的一部分)。在本命盘中,这一步受 yearDivide/horoscopeDivide 影响,用于确定“生年干支”以及后续四化、神煞与运限分界。

pillars = getHeavenlyStemAndEarthlyBranchBySolarDate(solarDate, tIndex, {year: config.yearDivide})
(yearStem, yearBranch) = pillars.yearly

4.4 Step 2:定命宫/身宫(getSoulAndBody

源码:src/astro/palace.ts

4.4.1 农历月份索引(考虑闰月修正)

源码:fixLunarMonthIndexsrc/utils/index.ts

说明:fixLunarMonthIndex 把农历月转换为“正月建寅”的月份索引(寅=0)。若遇闰月且 fixLeap=true,则以 15 日为界:前半月按上月、后半月按下月(晚子时另有特殊处理)。

function fixLunarMonthIndex(solarDate, timeIndex, fixLeap):
  lunar = solar2lunar(solarDate) // lunarMonth:1..12, lunarDay:1..30, isLeap
  needAdd = lunar.isLeap and fixLeap and lunar.lunarDay > 15 and timeIndex != 12
  return fixIndex(lunar.lunarMonth + 1 - 2 + (needAdd?1:0), 12)

4.4.2 命宫/身宫索引

说明:调用历法能力取干支(四柱的一部分)。在本命盘中,这一步受 yearDivide/horoscopeDivide 影响,用于确定“生年干支”以及后续四化、神煞与运限分界。

pillars = getHeavenlyStemAndEarthlyBranchBySolarDate(solarDate, timeIndex, {year:config.yearDivide, month:config.horoscopeDivide})
(timeBranch) = pillars.hourly[1]
monthIndex = fixLunarMonthIndex(solarDate, timeIndex, fixLeap)

soulIndex = fixIndex(monthIndex - indexOf(EARTHLY_BRANCHES, timeBranch), 12)
bodyIndex = fixIndex(monthIndex + indexOf(EARTHLY_BRANCHES, timeBranch), 12)

4.4.3 命宫干支

说明:命宫天干的计算链:先用“五虎遁”由年干推出寅月天干(可视作干支轮转起点),再按命宫索引在十天干上顺推得到命宫天干;命宫地支则是寅起顺排到命宫索引。

yinStem = TIGER_RULE[yearStem]

soulStemIndex = fixIndex(indexOf(HEAVENLY_STEMS, yinStem) + soulIndex, 10)
soulStem = HEAVENLY_STEMS[soulStemIndex]

soulBranch = EARTHLY_BRANCHES[ fixIndex(soulIndex + 2, 12) ]

return { soulIndex, bodyIndex, heavenlyStemOfSoul:soulStem, earthlyBranchOfSoul:soulBranch }

4.5 Step 3:定五行局(getFiveElementsClass

源码:src/astro/palace.ts

说明(getFiveElementsClass):根据【命宫(或from)干支】计算五行局。实现采用源码的“干支取数→相加超5减5→映射局数”的数学版,不依赖口诀记忆。输出为 water2nd/wood3rd/metal4th/earth5th/fire6th 之一。

function getFiveElementsClass(stemKey, branchKey):
  stemNumber = floor(indexOf(HEAVENLY_STEMS, stemKey)/2) + 1
  branchIdx = indexOf(EARTHLY_BRANCHES, branchKey)
  branchNumber = floor(fixIndex(branchIdx, 6)/2) + 1

  x = stemNumber + branchNumber
  while x > 5: x -= 5

  table = ['wood3rd','metal4th','water2nd','fire6th','earth5th']
  return table[x-1]

4.6 Step 4:安主星(紫微系/天府系)

源码:src/star/majorStar.ts + src/star/location.ts

4.6.1 紫微/天府起点:getStartIndex

说明(getStartIndex):计算紫微/天府的起星宫位。核心是用【农历日】与【局数(2..6)】求可整除点:不断增加 offset 直到 (day+offset)%局数==0,然后用商(quotient)与 offset 奇偶决定紫微落宫;天府与紫微相对。

起紫微星诀算法

  • 六五四三二,酉午亥辰丑,
  • 局数除日数,商数宫前走;
  • 若见数无余,便要起虎口,
  • 日数小於局,还直宫中守。 举例
  • 例一:27日出生木三局,以三除27,循环0次就可以整除,27➗3=9,从寅进9格,在戍安紫微。
  • 例二:13日出生火六局,以六除13,最少需要加5才能整除, 18➗8=3,从寅进3格为辰,添加数为5(奇数),故要逆回五宫,在亥安紫微。
  • 例三:6日出生土五局,以五除6,最少需要加4才能整除,10➗5=2,从寅进2格为卯,添加数为4(偶数),顺行4格为未,在未安紫微。
function getStartIndex(param):
  lunar = solar2lunar(param.solarDate)
  day = lunar.lunarDay

  maxDays = getTotalDaysOfLunarMonth(param.solarDate)
  if param.timeIndex == 12:
    day += 1
    if day > maxDays: day -= maxDays

  baseStem = param.from?.heavenlyStem ?? soulStem
  baseBranch = param.from?.earthlyBranch ?? soulBranch
  fiveClass = getFiveElementsClass(baseStem, baseBranch)
  k = FiveElementsClass[fiveClass]

  offset = 0
  while (day + offset) % k != 0:
    offset += 1

  quotient = floor((day + offset) / k) % 12
  ziweiIndex = quotient - 1

  if offset % 2 == 0:
    ziweiIndex = ziweiIndex + offset
  else:
    ziweiIndex = ziweiIndex - offset

  ziweiIndex = fixIndex(ziweiIndex, 12)
  tianfuIndex = fixIndex(12 - ziweiIndex, 12)

  return { ziweiIndex, tianfuIndex }

4.6.2 落星序列

紫微系(逆行):

ziweiGroup = [
  'ziweiMaj','tianjiMaj','', 'taiyangMaj','wuquMaj','tiantongMaj','', '', 'lianzhenMaj'
]

天府系(顺行):

tianfuGroup = [
  'tianfuMaj','taiyinMaj','tanlangMaj','jumenMaj','tianxiangMaj','tianliangMaj','qishaMaj',
  '', '', '', 'pojunMaj'
]

4.7 Step 5:安 14 辅星(getMinorStar

源码:src/star/minorStar.ts + src/star/location.ts

4.7.1 左辅/右弼(按生月)

说明(getZuoYouIndex):按生月定左辅右弼:左辅从辰顺数到月数;右弼从戌逆数到月数。输入月数为 1..12,输出两个宫位索引。

function getZuoYouIndex(lunarMonth_1_to_12):
  zuoIndex = fixIndex(fixEarthlyBranchIndex('chenEarthly') + (lunarMonth_1_to_12 - 1))
  youIndex = fixIndex(fixEarthlyBranchIndex('xuEarthly')   - (lunarMonth_1_to_12 - 1))
  return {zuoIndex,youIndex}

4.7.2 文昌/文曲(按时支)

说明(getChangQuIndex):按时辰定文昌文曲:文昌戌上逆时,文曲辰上顺时。输入 timeIndex(0..12),内部先 fixIndex 后计算。

function getChangQuIndex(timeIndex):
  t = fixIndex(timeIndex)
  changIndex = fixIndex(fixEarthlyBranchIndex('xuEarthly')   - t)
  quIndex    = fixIndex(fixEarthlyBranchIndex('chenEarthly') + t)
  return {changIndex,quIndex}

4.7.3 天魁/天钺(按年干)

说明(getKuiYueIndex):按年干定天魁天钺:把年干分组映射到固定两地支(魁/钺),再转换为宫位索引。

function getKuiYueIndex(yearStem):
  if yearStem in [jia, wu, geng]: return {kui:'chouEarthly', yue:'weiEarthly'}
  if yearStem in [yi, ji]:       return {kui:'ziEarthly',   yue:'shenEarthly'}
  if yearStem == xin:            return {kui:'wuEarthly',   yue:'yinEarthly'}
  if yearStem in [bing, ding]:   return {kui:'haiEarthly',  yue:'youEarthly'}
  if yearStem in [ren, gui]:     return {kui:'maoEarthly',  yue:'siEarthly'}

4.7.4 禄存/擎羊/陀罗/天马(按年干/年支)

说明(getLuYangTuoMaIndex):按年干定禄存,再以前后宫推擎羊(+1)/陀罗(-1);按年支分组定天马(四马地)。输出禄/羊/陀/马四个索引。

function getLuYangTuoMaIndex(yearStem, yearBranch):
  // 天马
  if yearBranch in [yin,wu,xu]:   ma='shenEarthly'
  if yearBranch in [shen,zi,chen]:ma='yinEarthly'
  if yearBranch in [si,you,chou]: ma='haiEarthly'
  if yearBranch in [hai,mao,wei]: ma='siEarthly'

  // 禄存
  switch yearStem:
    jia -> lu='yinEarthly'
    yi  -> lu='maoEarthly'
    bing/wu -> lu='siEarthly'
    ding/ji -> lu='wuEarthly'
    geng -> lu='shenEarthly'
    xin  -> lu='youEarthly'
    ren  -> lu='haiEarthly'
    gui  -> lu='ziEarthly'

  luIndex = fixEarthlyBranchIndex(lu)
  maIndex = fixEarthlyBranchIndex(ma)
  return {luIndex, maIndex, yangIndex:fixIndex(luIndex+1), tuoIndex:fixIndex(luIndex-1)}

4.7.5 火星/铃星(按年支+时支)

说明(getHuoLingIndex):按年支先定火星/铃星“子时起点宫”,再顺数到出生时辰(加 timeIndex)。

function getHuoLingIndex(yearBranch, timeIndex):
  t = fixIndex(timeIndex)
  if yearBranch in [yin,wu,xu]:
    huoStart='chouEarthly'; lingStart='maoEarthly'
  if yearBranch in [shen,zi,chen]:
    huoStart='yinEarthly'; lingStart='xuEarthly'
  if yearBranch in [si,you,chou]:
    huoStart='maoEarthly'; lingStart='xuEarthly'
  if yearBranch in [hai,mao,wei]:
    huoStart='youEarthly'; lingStart='xuEarthly'

  return {
    huoIndex:  fixIndex(fixEarthlyBranchIndex(huoStart)  + t),
    lingIndex: fixIndex(fixEarthlyBranchIndex(lingStart) + t)
  }

4.7.6 地空/地劫(按时支)

说明(getKongJieIndex):按时辰定地空地劫:以亥宫为基点,地劫顺数、地空逆数到时辰。

function getKongJieIndex(timeIndex):
  t = fixIndex(timeIndex)
  hai = fixEarthlyBranchIndex('haiEarthly')
  return { kongIndex: fixIndex(hai - t), jieIndex: fixIndex(hai + t) }

4.8 Step 6:安杂耀(getAdjectiveStar

源码:src/star/adjectiveStar.ts + src/star/location.ts

实现建议:先实现 5 个索引函数,再逐颗落星:

  • getYearlyStarIndex(param):年系杂耀(最复杂)
  • getMonthlyStarIndex(solarDate, timeIndex, fixLeap):月系杂耀
  • getDailyStarIndex(solarDate, timeIndex, fixLeap):日系杂耀
  • getTimelyStarIndex(timeIndex):时系杂耀
  • getLuanXiIndex(yearBranch):红鸾/天喜

4.8.1 红鸾/天喜(按年支)

说明(getLuanXiIndex):按年支定红鸾:卯上起子逆数到太岁;天喜为其对宫(+6)。

function getLuanXiIndex(yearBranch):
  hongluanIndex = fixIndex(fixEarthlyBranchIndex('maoEarthly') - indexOf(EARTHLY_BRANCHES, yearBranch))
  tianxiIndex = fixIndex(hongluanIndex + 6)
  return {hongluanIndex,tianxiIndex}

4.8.2 月系索引

说明(getMonthlyStarIndex):月系杂耀落宫:先把公历换算出“月索引(寅为0)”,再按源码映射表/线性偏移计算月解、天姚、天刑、阴煞、天月、天巫。

function getMonthlyStarIndex(solarDate, timeIndex, fixLeap):
  monthIndex = fixLunarMonthIndex(solarDate, timeIndex, fixLeap)

  mapJieshen = ['shenEarthly','xuEarthly','ziEarthly','yinEarthly','chenEarthly','wuEarthly']
  yuejieIndex = fixIndex(fixEarthlyBranchIndex(mapJieshen[floor(monthIndex/2)]))

  tianyaoIndex  = fixIndex(fixEarthlyBranchIndex('chouEarthly') + monthIndex)
  tianxingIndex = fixIndex(fixEarthlyBranchIndex('youEarthly')  + monthIndex)

  yinshaMap = ['yinEarthly','ziEarthly','xuEarthly','shenEarthly','wuEarthly','chenEarthly']
  yinshaIndex = fixIndex(fixEarthlyBranchIndex(yinshaMap[monthIndex % 6]))

  tianyueMap = ['xuEarthly','siEarthly','chenEarthly','yinEarthly','weiEarthly','maoEarthly','haiEarthly','weiEarthly','yinEarthly','wuEarthly','xuEarthly','yinEarthly']
  tianyueIndex = fixIndex(fixEarthlyBranchIndex(tianyueMap[monthIndex]))

  tianwuMap = ['siEarthly','shenEarthly','yinEarthly','haiEarthly']
  tianwuIndex = fixIndex(fixEarthlyBranchIndex(tianwuMap[monthIndex % 4]))

  return {yuejieIndex,tianyaoIndex,tianxingIndex,yinshaIndex,tianyueIndex,tianwuIndex}

4.8.3 日系索引

说明:日系杂耀需要一个“日索引”。本库规则:晚子时(timeIndex>=12)算当天的第 lunarDay 日,否则算 lunarDay-1(相当于把早子归入前一日的尾部)。

function fixLunarDayIndex(lunarDay, timeIndex):
  return (timeIndex >= 12) ? lunarDay : (lunarDay - 1)

// 说明(getDailyStarIndex):日系杂耀落宫:以“日索引(dayIndex)”为核心(晚子时不减1),由左辅/右弼/文昌/文曲起初一顺/逆推到生日,得到三台/八座/恩光/天贵。
function getDailyStarIndex(solarDate, timeIndex, fixLeap):
  lunarDay = solar2lunar(solarDate).lunarDay
  monthIndex = fixLunarMonthIndex(solarDate, timeIndex, fixLeap)

  (zuoIndex,youIndex) = getZuoYouIndex(monthIndex + 1)
  (changIndex,quIndex) = getChangQuIndex(timeIndex)

  dayIndex = fixLunarDayIndex(lunarDay, timeIndex)

  santaiIndex  = fixIndex((zuoIndex + dayIndex) % 12)
  bazuoIndex   = fixIndex((youIndex - dayIndex) % 12)
  enguangIndex = fixIndex(((changIndex + dayIndex) % 12) - 1)
  tianguiIndex = fixIndex(((quIndex + dayIndex) % 12) - 1)

  return {santaiIndex,bazuoIndex,enguangIndex,tianguiIndex}

4.8.4 时系索引

说明(getTimelyStarIndex):时系杂耀落宫:台辅(午起)与封诰(寅起)顺数到出生时辰(加 timeIndex)。

function getTimelyStarIndex(timeIndex):
  t = fixIndex(timeIndex)
  taifuIndex = fixIndex(fixEarthlyBranchIndex('wuEarthly') + t)
  fenggaoIndex = fixIndex(fixEarthlyBranchIndex('yinEarthly') + t)
  return {taifuIndex,fenggaoIndex}

4.8.5 年系索引(完整公式)

源码:getYearlyStarIndex(param)

说明(getYearlyStarIndex):年系杂耀落宫总汇:以 horoscopeDivide 分界取流年干支,再结合命/身宫索引、固定映射表与线性偏移计算多颗年系星(华盖/咸池/孤寡/天厨/旬空等),并处理中州派差异。

function getYearlyStarIndex(param):
  // 年系杂耀使用 horoscopeDivide 作为年分界
  pillars = getHeavenlyStemAndEarthlyBranchBySolarDate(param.solarDate, param.timeIndex, {year: config.horoscopeDivide})
  (yearStem, yearBranch) = pillars.yearly

  (soulIndex, bodyIndex) = getSoulAndBody({solarDate:param.solarDate,timeIndex:param.timeIndex,fixLeap:param.fixLeap}).(soulIndex,bodyIndex)

  stemIdx = indexOf(HEAVENLY_STEMS, yearStem)
  branchIdx = indexOf(EARTHLY_BRANCHES, yearBranch)

  // 华盖/咸池
  (huagaiIndex,xianchiIndex) = getHuagaiXianchiIndex(yearBranch)
  // 孤辰/寡宿
  (guchenIndex,guasuIndex) = getGuGuaIndex(yearBranch)

  tiancaiIndex  = fixIndex(soulIndex + branchIdx)
  tianshouIndex = fixIndex(bodyIndex + branchIdx)

  tianchuMap = ['siEarthly','wuEarthly','ziEarthly','siEarthly','wuEarthly','shenEarthly','yinEarthly','wuEarthly','youEarthly','haiEarthly']
  tianchuIndex = fixIndex(fixEarthlyBranchIndex(tianchuMap[stemIdx]))

  posuiMap = ['siEarthly','chouEarthly','youEarthly']
  posuiIndex = fixIndex(fixEarthlyBranchIndex(posuiMap[branchIdx % 3]))

  feilianMap = ['shenEarthly','youEarthly','xuEarthly','siEarthly','wuEarthly','weiEarthly','yinEarthly','maoEarthly','chenEarthly','haiEarthly','ziEarthly','chouEarthly']
  feilianIndex = fixIndex(fixEarthlyBranchIndex(feilianMap[branchIdx]))

  longchiIndex = fixIndex(fixEarthlyBranchIndex('chenEarthly') + branchIdx)
  fenggeIndex  = fixIndex(fixEarthlyBranchIndex('xuEarthly')   - branchIdx)

  tiankuIndex = fixIndex(fixEarthlyBranchIndex('wuEarthly') - branchIdx)
  tianxuIndex = fixIndex(fixEarthlyBranchIndex('wuEarthly') + branchIdx)

  tianguanMap = ['weiEarthly','chenEarthly','siEarthly','yinEarthly','maoEarthly','youEarthly','haiEarthly','youEarthly','xuEarthly','wuEarthly']
  tianguanIndex = fixIndex(fixEarthlyBranchIndex(tianguanMap[stemIdx]))

  tianfuMap = ['youEarthly','shenEarthly','ziEarthly','haiEarthly','maoEarthly','yinEarthly','wuEarthly','siEarthly','wuEarthly','siEarthly']
  tianfuIndex = fixIndex(fixEarthlyBranchIndex(tianfuMap[stemIdx]))

  tiandeIndex = fixIndex(fixEarthlyBranchIndex('youEarthly') + branchIdx)
  yuedeIndex  = fixIndex(fixEarthlyBranchIndex('siEarthly')  + branchIdx)

  tiankongIndex = fixIndex(fixEarthlyBranchIndex(yearBranch) + 1)

  jieluMap    = ['shenEarthly','wuEarthly','chenEarthly','yinEarthly','ziEarthly']
  kongwangMap = ['youEarthly','weiEarthly','siEarthly','maoEarthly','chouEarthly']
  jieluIndex    = fixIndex(fixEarthlyBranchIndex(jieluMap[stemIdx % 5]))
  kongwangIndex = fixIndex(fixEarthlyBranchIndex(kongwangMap[stemIdx % 5]))

  xunkongIndex = fixIndex(
    fixEarthlyBranchIndex(yearBranch)
    + indexOf(HEAVENLY_STEMS,'guiHeavenly')
    - indexOf(HEAVENLY_STEMS, yearStem)
    + 1
  )

  yinyang = branchIdx % 2
  if yinyang != (xunkongIndex % 2):
    xunkongIndex = fixIndex(xunkongIndex + 1)

  jiekongIndex = (yinyang == 0) ? jieluIndex : kongwangIndex

  jieshaAdjIndex = getJieshaAdjIndex(yearBranch)
  nianjieIndex = getNianjieIndex(yearBranch)
  dahaoAdjIndex = getDahaoIndex(yearBranch)

  // 天伤/天使(相对命宫)
  tianshangIndex = fixIndex(indexOf(PALACES,'friendsPalace') + soulIndex)
  tianshiIndex   = fixIndex(indexOf(PALACES,'healthPalace')  + soulIndex)

  genderParity = (param.gender == 'male') ? 0 : 1
  sameYinyang = (yinyang == genderParity)
  if config.algorithm == 'zhongzhou' and !sameYinyang:
    swap(tianshiIndex, tianshangIndex)

  return {...all indices...}

并实现以下子函数:

说明(getHuagaiXianchiIndex):按年支分组映射华盖/咸池到固定地支,再转为宫位索引。

function getHuagaiXianchiIndex(yearBranch):
  if yearBranch in [yin,wu,xu]:   hg='xuEarthly';  xc='maoEarthly'
  if yearBranch in [shen,zi,chen]:hg='chenEarthly';xc='youEarthly'
  if yearBranch in [si,you,chou]: hg='chouEarthly';xc='wuEarthly'
  if yearBranch in [hai,wei,mao]: hg='weiEarthly'; xc='ziEarthly'
  return {huagaiIndex:fixIndex(fixEarthlyBranchIndex(hg)), xianchiIndex:fixIndex(fixEarthlyBranchIndex(xc))}

// 说明(getGuGuaIndex):按年支分组映射孤辰/寡宿到固定地支,再转为宫位索引。
function getGuGuaIndex(yearBranch):
  if yearBranch in [yin,mao,chen]: gu='siEarthly';  gua='chouEarthly'
  if yearBranch in [si,wu,wei]:    gu='shenEarthly';gua='chenEarthly'
  if yearBranch in [shen,you,xu]:  gu='haiEarthly'; gua='weiEarthly'
  if yearBranch in [hai,zi,chou]:  gu='yinEarthly'; gua='xuEarthly'
  return {guchenIndex:fixIndex(fixEarthlyBranchIndex(gu)), guasuIndex:fixIndex(fixEarthlyBranchIndex(gua))}

// 说明(getJieshaAdjIndex):(中州派用)按年支分四组返回劫杀固定宫位索引(0/3/6/9)。
function getJieshaAdjIndex(yearBranch):
  if yearBranch in [shen,zi,chen]: return 3
  if yearBranch in [hai,mao,wei]:  return 6
  if yearBranch in [yin,wu,xu]:    return 9
  if yearBranch in [si,you,chou]:  return 0

// 说明(getNianjieIndex):按年支定年解:戌上起子逆数到太岁(源码用 12 项映射表实现)。
function getNianjieIndex(yearBranch):
  branchIdx = indexOf(EARTHLY_BRANCHES, yearBranch)
  map = ['xuEarthly','youEarthly','shenEarthly','weiEarthly','wuEarthly','siEarthly','chenEarthly','maoEarthly','yinEarthly','chouEarthly','ziEarthly','haiEarthly']
  return fixIndex(fixEarthlyBranchIndex(map[branchIdx]))

// 说明(getDahaoIndex):(中州派用)按年支先取“对冲位”,再按阴阳移位规则折算到宫位索引(源码用映射表 + -2 对齐实现)。
function getDahaoIndex(yearBranch):
  branchIdx = indexOf(EARTHLY_BRANCHES, yearBranch)
  matched = ['weiEarthly','wuEarthly','youEarthly','shenEarthly','haiEarthly','xuEarthly','chouEarthly','ziEarthly','maoEarthly','yinEarthly','siEarthly','chenEarthly'][branchIdx]
  return fixIndex(indexOf(EARTHLY_BRANCHES, matched) - 2)

4.9 Step 7:长生12神 / 博士12神 / 流年12神

源码:src/star/decorativeStar.ts

4.9.1 长生12神

起点(按五行局数值):

  • 2(水二局)→ 申
  • 3(木三局)→ 亥
  • 4(金四局)→ 巳
  • 5(土五局)→ 申
  • 6(火六局)→ 寅

序列:

['changsheng','muyu','guandai','linguan','diwang','shuai','bing','si','mu','jue','tai','yang']

顺逆:若 genderYinYang == yearBranchYinYang 顺行,否则逆行。

4.9.2 博士12神

起点:禄存宫 luIndex

序列:

['boshi','lishi','qinglong','xiaohao','jiangjun','zhoushu','faylian','xishen','bingfu','dahao','fubing','guanfu']

顺逆同上。

4.9.3 流年12神(岁前/将前)

岁前12神:从 fixEarthlyBranchIndex(yearBranch) 起顺排。

  • algorithm=='zhongzhou':将 dahao 替换为 suipo

将前12神:先按年支确定将星起点:

  • 寅午戌→午,申子辰→子,巳酉丑→酉,亥卯未→卯 再从该起点顺排固定序列。

5. 起大限/小限:getHoroscope

这一节负责把“时间(年龄)”映射为“宫位”:

  • 大限:每 10 年一宫,从命宫起,按【性别阴阳 vs 年支阴阳】决定顺逆;并且会给每个大限配上对应的干支。
  • 小限:每 1 年一宫,先由年支决定起点宫,再按男女顺逆把年龄序列分配到 12 宫。

源码:src/astro/palace.ts getHoroscope(param)

5.1 输入/输出

输入:AstrolabeParam

  • solarDate, timeIndex, gender, fixLeap
  • 可选:from:{heavenlyStem, earthlyBranch}(地盘/人盘重排时使用;不传则按本命命宫干支)

输出:

  • decadals[12]:每宫对应的大限 { range:[startAge, startAge+9], heavenlyStem, earthlyBranch }
  • ages[12]:每宫对应的小限年龄数组(10 个:1,13,25,...)

5.2 大限(十年一宫)精确算法

规则:

  • 大限从命宫起,每十年一宫。
  • 阳男阴女顺行阴男阳女逆行
  • 起运年龄 = 五行局数值(2..6)。

源码等价伪代码:

说明(getHoroscope):同时计算大限与小限:大限以命宫起、每十年一宫,并给出每宫大限干支;小限按年支定起点宫后把年龄序列映射到 12 宫(男顺女逆)。

function getHoroscope(param):
  decadals = array(12)

  // 1) 生年干支(用于起大限干支)
  yearly = getHeavenlyStemAndEarthlyBranchBySolarDate(param.solarDate, param.timeIndex, {year: config.yearDivide}).yearly
  (yearStem, yearBranch) = yearly

  // 2) 命/身宫(用于命宫起运)
  (soulIndex, _, soulStem, soulBranch) = getSoulAndBody(param)

  // 3) 五行局:若传 from 则以 from 起局,否则以命宫起局
  baseStem = param.from?.heavenlyStem ?? soulStem
  baseBranch = param.from?.earthlyBranch ?? soulBranch
  fiveClass = getFiveElementsClass(baseStem, baseBranch)
  startAge = FiveElementsClass[fiveClass]  // 2..6

  // 4) 判断顺逆:源码用 “性别阴阳 == 年支阴阳” 来判顺行
  // yearBranchYinYang 可直接由 earthlyBranches[yearBranch].yinYang 获取
  yearBranchYinYang = earthlyBranches[yearBranch].yinYang
  genderYinYang = (param.gender == 'male') ? '阳' : '阴'
  forward = (genderYinYang == yearBranchYinYang)

  // 5) 五虎遁取“寅起始天干”(用于推每宫的大限干支)
  startStem = TIGER_RULE[yearStem]

  for i in 0..11:
    idx = forward ? fixIndex(soulIndex + i) : fixIndex(soulIndex - i)

    // i 为第几个大限(第0个、1个...),rangeStart 逐个 +10
    rangeStart = startAge + 10*i

    stemIndex = fixIndex(indexOf(HEAVENLY_STEMS, startStem) + idx, 10)
    branchIndex = fixIndex(indexOf(EARTHLY_BRANCHES, 'yinEarthly') + idx, 12)

    decadals[idx] = {
      range: [rangeStart, rangeStart+9],
      heavenlyStem: HEAVENLY_STEMS[stemIndex],
      earthlyBranch: EARTHLY_BRANCHES[branchIndex]
    }

  // ages 在 5.3 计算
  return {decadals, ages}

5.3 小限(每年一宫)精确算法

5.3.1 年支定小限起点(getAgeIndex

源码:src/utils/index.ts getAgeIndex(yearBranch)

说明(getAgeIndex):由年支确定小限起点宫(四局分组:寅午戌/申子辰/巳酉丑/亥卯未),返回宫位索引。

function getAgeIndex(yearBranch):
  if yearBranch in [yin,wu,xu]:   return fixEarthlyBranchIndex('chenEarthly')
  if yearBranch in [shen,zi,chen]:return fixEarthlyBranchIndex('xuEarthly')
  if yearBranch in [si,you,chou]: return fixEarthlyBranchIndex('weiEarthly')
  if yearBranch in [hai,mao,wei]: return fixEarthlyBranchIndex('chouEarthly')

5.3.2 将年龄序列映射到 12 宫

源码等价伪代码:

说明:这是小限“年龄序列 → 宫位”的最终映射:先得到起点宫 ageIdx,再为每个 i 生成年龄序列 [i+1, i+13, i+25, ...],最后按男顺女逆把序列放入 12 宫数组。

ageIdx = getAgeIndex(yearBranch)
ages = array(12)

for i in 0..11:
  ageList = []
  for j in 0..9:
    ageList.push(12*j + i + 1)

  // 男顺女逆(源码用 gender key 判断)
  idx = (param.gender == 'male') ? fixIndex(ageIdx + i) : fixIndex(ageIdx - i)
  ages[idx] = ageList

6. bySolar:从输入到星盘对象的可复刻流程

上一节给了逐步公式,这一节把它串成真正可落地的“实现流程”。建议你实现时就按这里的顺序组织代码:先计算一次性的全局量(命/身宫、起星点、各类星曜数组、运限数组),再循环 12 次构造宫对象;这样最不容易写错索引。

源码:src/astro/astro.ts bySolar()

6.1 总览

你可以把 bySolar 视为 3 段:

  1. 计算所有“全局一次性结果”(命/身宫、星曜数组、运限数组等)
  2. 循环 12 次组装宫位对象(每宫天干地支 + 落星 + 运限)
  3. 组装星盘元信息(干支历、星座生肖、命主身主、五行局等)

6.2 关键步骤伪代码

说明:bySolar 是“天盘排盘编排器”的完整流水线伪代码:它串起生年干支、命身宫、各类安星函数、12神、大限小限,并最终组装成 12 宫数组与星盘元信息。

function bySolar(solarDate, timeIndex, gender, fixLeap=true):
  // Step0: 晚子时日界
  if config.dayDivide == 'current' and timeIndex >= 12:
    tIndex = 0
  else:
    tIndex = timeIndex

  // Step1: 生年干支(yearDivide)
  yearly = getHeavenlyStemAndEarthlyBranchBySolarDate(solarDate, tIndex, {year: config.yearDivide}).yearly
  (yearStem, yearBranch) = yearly

  // Step2: 命/身宫与命宫干支
  (soulIndex, bodyIndex, soulStem, soulBranch) = getSoulAndBody({solarDate, timeIndex:tIndex, gender, fixLeap})

  // Step3: 12宫宫名(命宫旋转)
  palaceNames = getPalaceNames(soulIndex)

  // Step4: 星曜(每项返回 12 个数组,对应 12 宫)
  majorStars = getMajorStar({solarDate, timeIndex:tIndex, fixLeap})
  minorStars = getMinorStar(solarDate, tIndex, fixLeap)
  adjectiveStars = getAdjectiveStar({solarDate, timeIndex:tIndex, gender, fixLeap})

  // Step5: 12神
  changsheng12 = getchangsheng12({solarDate, timeIndex:tIndex, gender, fixLeap})
  boshi12 = getBoShi12(solarDate, gender)
  (jiangqian12, suiqian12) = getYearly12(solarDate)

  // Step6: 大限/小限
  (decadals, ages) = getHoroscope({solarDate, timeIndex:tIndex, gender, fixLeap})

  // Step7: 循环生成 12 宫
  palaces = []
  for i in 0..11:
    palaceStem = HEAVENLY_STEMS[ fixIndex(indexOf(HEAVENLY_STEMS, soulStem) - soulIndex + i, 10) ]
    palaceBranch = EARTHLY_BRANCHES[ fixIndex(2 + i, 12) ]

    palaces.push({
      index: i,
      name: palaceNames[i],
      isBodyPalace: (i == bodyIndex),
      isOriginalPalace: (palaceBranch not in ['ziEarthly','chouEarthly']) and (palaceStem == yearStem),
      heavenlyStem: palaceStem,
      earthlyBranch: palaceBranch,
      majorStars: majorStars[i],
      minorStars: minorStars[i],
      adjectiveStars: adjectiveStars[i],
      changsheng12: changsheng12[i],
      boshi12: boshi12[i],
      jiangqian12: jiangqian12[i],
      suiqian12: suiqian12[i],
      decadal: decadals[i],
      ages: ages[i]
    })

  // Step8: 命宫/身宫地支(注意:宫位从寅起,所以 +2 对齐)
  soulPalaceBranch = EARTHLY_BRANCHES[fixIndex(soulIndex + 2)]
  bodyPalaceBranch = EARTHLY_BRANCHES[fixIndex(bodyIndex + 2)]

  // Step9: 命主/身主
  if config.algorithm == 'zhongzhou':
    soulStar = earthlyBranches[yearBranch].soul
  else:
    soulStar = earthlyBranches[soulPalaceBranch].soul
  bodyStar = earthlyBranches[yearBranch].body

  // Step10: 五行局(以命宫干支)
  fiveElementsClass = getFiveElementsClass(soulStem, soulBranch)

  return Astrolabe{...}

7. 命主/身主(soul/body)

源码:src/data/earthlyBranches.ts + src/astro/astro.ts

  • 身主:按生年支:earthlyBranches[yearBranch].body
  • 命主:
    • 中州派:按生年支(earthlyBranches[yearBranch].soul
    • 通行:按命宫地支(earthlyBranches[soulPalaceBranch].soul

这两个字段本质都是“星 key”,其映射表在 src/data/earthlyBranches.ts


8. 四化与亮度(庙旺落陷)可复刻实现

四化与亮度都是“附着在星曜上的属性”:

  • 四化:由【某个天干】决定一组【禄/权/科/忌 对应的四颗星】,然后星曜会根据自己是否在该组中得到对应化。
  • 亮度:由【星曜在 12 宫中的位置】决定其状态(庙旺落陷等)。

源码:src/utils/index.ts + src/data/heavenlyStems.ts + src/data/stars.ts

8.1 四化表(禄权科忌)

默认:heavenlyStems[stem].mutagen(长度固定 4,对应【禄 权 科 忌】) 可覆盖:config.mutagens[stem]

说明(getTargetMutagens):四化配置读取:优先使用 config.mutagens 覆盖表,否则回退到默认 heavenlyStems[stem].mutagen。输出为长度 4 的星曜数组(禄权科忌对应星)。

function getTargetMutagens(stem):
  return config.mutagens[stem] ?? heavenlyStems[stem].mutagen

// 说明(getMutagen):给定【星曜 + 天干】,判断该星在四化表中的位置并返回对应化(禄/权/科/忌);不在表内返回空。
function getMutagen(starKey, stem):
  target = getTargetMutagens(stem) // 4 items
  idx = indexOf(target, starKey)
  if idx < 0: return ''
  return MUTAGEN[idx] // MUTAGEN 的顺序与源码一致:['lu','quan','ke','ji'](实现时只需保持同序)

// 说明(getMutagensByHeavenlyStem):直接返回某天干的四化“对应星曜”列表(长度 4,顺序禄权科忌)。
function getMutagensByHeavenlyStem(stem):
  return getTargetMutagens(stem)

8.2 亮度表

默认:STARS_INFO[star].brightness[12] 可覆盖:config.brightness[star]

说明(getBrightness):根据【星曜 + 宫位索引】读取亮度数组(可被 config.brightness 覆盖),返回该宫位的亮度字符串。

function getBrightness(starKey, palaceIndex):
  arr = config.brightness[starKey] ?? STARS_INFO[starKey].brightness
  if arr is empty: return ''
  return arr[fixIndex(palaceIndex)]

9. 运限(大限/小限/流年/流月/流日/流时)完整算法

运限不是重新排一张盘,而是在本命盘的 12 宫坐标系上:对给定目标日期,算出此刻的【虚岁】、落在哪个【大限/小限宫】、以及【流年/月/日/时】各自落宫。落宫索引算出后,再用“以该宫为命宫旋转的宫名表”生成该运限的十二宫命名,并叠加对应的“流耀”。

源码:src/astro/FunctionalAstrolabe.ts _getHoroscopeBySolarDate()

9.1 输入

  • 本命盘对象 astrolabe(含 solarDaterawDates
  • targetDate(默认当前时间)
  • 可选 timeIndex(不传则 timeToIndex(hour)

9.2 目标日期干支(horoscopeDivide)

说明:运限计算的第一步:把目标日期时间转换为时辰索引(或使用调用方显式传入的时辰),然后按 horoscopeDivide 取目标日期的年/月/日/时干支。

convertTimeIndex = timeToIndex(hour(targetDate))
useTimeIndex = timeIndex ?? convertTimeIndex

pillars = getHeavenlyStemAndEarthlyBranchBySolarDate(targetDate, useTimeIndex, {
  year: config.horoscopeDivide,
  month: config.horoscopeDivide
})
(yearly, monthly, daily, hourly) = (pillars.yearly, pillars.monthly, pillars.daily, pillars.hourly)

9.3 虚岁 nominalAge(ageDivide)

源码等价伪代码:

说明:虚岁计算:先把出生与目标日期都转为农历,再按配置决定“自然年+1”或“过了农历生日才+1”。输出 nominalAge 用于定位大限/小限。

birth = solar2lunar(astrolabe.solarDate)
date  = solar2lunar(targetDate)

nominalAge = date.lunarYear - birth.lunarYear

if config.ageDivide == 'birthday':
  // 过了农历生日才 +1
  if (
    (date.lunarYear == birth.lunarYear and date.lunarMonth == birth.lunarMonth and date.lunarDay > birth.lunarDay)
    or (date.lunarMonth > birth.lunarMonth)
  ):
    nominalAge += 1
else:
  // 自然年分界直接 +1
  nominalAge += 1

9.4 大限落宫(含童限 fallback)

说明:以下伪代码给出该步骤的精确计算公式与中间变量含义;即使不实现同名函数,也应保持等价的输入输出关系与取模规则。

// 1) 先按本命盘里每宫的 decadal.range 查找
find palaceIndex such that nominalAge in astrolabe.palaces[palaceIndex].decadal.range

if not found:
  // 2) 童限:1..6 岁映射到固定宫名
  mapping = ['命宫','财帛','疾厄','夫妻','福德','官禄']
  palaceName = mapping[nominalAge - 1]
  palaceIndex = astrolabe.palace(palaceName).index
  isChildhood = true
else:
  isChildhood = false

9.5 小限落宫

说明:小限定位:在本命盘 12 宫中查找哪个宫的 ages 序列包含 nominalAge,该宫即为当年的小限宫。

find ageIndex such that nominalAge in astrolabe.palaces[ageIndex].ages

9.6 流年/流月/流日/流时落宫索引

说明:流年/流月/流日/流时落宫:流年由目标年支直接折算索引;流月用“流年地支逆数到生月宫,再顺数到生时宫为正月起点”的公式实现,并额外处理出生与当月的闰月前后半月;流日/流时在流月基础上继续顺推。

yearlyIndex = fixEarthlyBranchIndex(yearly[1])

birthLeapAdd = (birth.isLeap and birth.lunarDay > 15) ? 1 : 0
currLeapAdd  = (date.isLeap  and date.lunarDay  > 15) ? 1 : 0

birthHourBranch = astrolabe.rawDates.chineseDate.hourly[1]

monthlyIndex = fixIndex(
  yearlyIndex
  - (birth.lunarMonth + birthLeapAdd)
  + indexOf(EARTHLY_BRANCHES, birthHourBranch)
  + (date.lunarMonth + currLeapAdd)
)

dailyIndex = fixIndex(monthlyIndex + date.lunarDay - 1)

hourlyIndex = fixIndex(dailyIndex + indexOf(EARTHLY_BRANCHES, hourly[1]))

9.7 运限“十二宫旋转”与四化星

任何 scope(大限/小限/流年/月/日/时)得到 index 后:

说明:以下伪代码给出该步骤的精确计算公式与中间变量含义;即使不实现同名函数,也应保持等价的输入输出关系与取模规则。

palaceNames = getPalaceNames(index)
mutagenStars = getMutagensByHeavenlyStem(scopeStem)

9.8 运限流耀(魁钺昌曲禄羊陀马鸾喜)

源码:src/star/horoscopeStar.ts

核心:索引算法与“辅星索引”一致,但“流昌流曲”改为按天干映射(不是按时辰)。

9.8.1 getChangQuIndexByHeavenlyStem(stem)

源码:src/star/location.ts getChangQuIndexByHeavenlyStem

说明:以下伪代码给出该步骤的精确计算公式与中间变量含义;即使不实现同名函数,也应保持等价的输入输出关系与取模规则。

if stem==jia: chang=si,  qu=you
if stem==yi:  chang=wu,  qu=shen
if stem in [bing,wu]: chang=shen, qu=wu
if stem in [ding,ji]: chang=you,  qu=si
if stem==geng: chang=hai, qu=mao
if stem==xin:  chang=zi,  qu=yin
if stem==ren:  chang=yin, qu=zi
if stem==gui:  chang=mao, qu=hai

return {changIndex:fixIndex(fixEarthlyBranchIndex(chang)), quIndex:fixIndex(fixEarthlyBranchIndex(qu))}

9.8.2 getHoroscopeStar(stem, branch, scope)

索引来源:

  • 魁/钺:getKuiYueIndex(stem)
  • 昌/曲:getChangQuIndexByHeavenlyStem(stem)
  • 禄/羊/陀/马:getLuYangTuoMaIndex(stem, branch)
  • 鸾/喜:getLuanXiIndex(branch)

特别:若 scope == 'yearly',还会额外加入“年解”:

  • nianjieIndex = getNianjieIndex(branch)

注意:源码对不同 scope 只是改变“星名显示前缀”(运昌/流昌/日昌等);落宫索引完全一致

9.9 运限对象最终结构

  • decadal/yearly/monthly/daily/hourly:包含 stars = getHoroscopeStar(scopeStem, scopeBranch, scope)
  • age:不带 stars
  • yearly:额外带 yearlyDecStar = getYearly12(targetDate)

10. 地盘/人盘重排(中州派扩展)

地盘/人盘可以理解为:不改变出生时间,但更换“五行局起点的干支”,从而让主星/长生/大限等围绕新的命宫重新计算。实现上是“先生成天盘,再选择某宫(身宫/福德)作为新的命宫起点,覆盖更新部分字段”。

源码:withOptions() + rearrangeAstrolable()

  • 地盘:取身宫干支作为 from,重排
  • 人盘:取福德宫干支作为 from,重排

重排覆盖:宫名、主星、长生12神、大限/小限、身宫标记、五行局等(与源码一致)。


11. 源码速查

  • 天盘入口:src/astro/astro.ts bySolar()
  • 命/身宫、五行局、大限/小限:src/astro/palace.ts
  • 主星:src/star/majorStar.ts
  • 辅星/杂耀索引:src/star/location.ts
  • 辅星:src/star/minorStar.ts
  • 杂耀组装:src/star/adjectiveStar.ts
  • 长生/博士/流年12神:src/star/decorativeStar.ts
  • 运限:src/astro/FunctionalAstrolabe.ts
  • 运限流耀:src/star/horoscopeStar.ts