Files
hera-web/src/utils/fiveElements.ts
T

91 lines
3.1 KiB
TypeScript

import { Solar } from 'lunar-typescript'
import { ElementScore } from '../services/request'
type ElementName = '木' | '火' | '土' | '金' | '水'
export type FiveElementReport = {
scores: ElementScore[]
favorable: string
unfavorable: string
dayMaster: string
}
const stemElements: Record<string, ElementName> = {
甲: '木', 乙: '木', 丙: '火', 丁: '火', 戊: '土', 己: '土', 庚: '金', 辛: '金', 壬: '水', 癸: '水'
}
const branchElements: Record<string, ElementName> = {
子: '水', 丑: '土', 寅: '木', 卯: '木', 辰: '土', 巳: '火', 午: '火', 未: '土', 申: '金', 酉: '金', 戌: '土', 亥: '水'
}
const elementMeta: Record<ElementName, { color: string }> = {
木: { color: '#36b85b' },
火: { color: '#ef5a58' },
土: { color: '#b46a18' },
金: { color: '#d59a05' },
水: { color: '#35bfe8' }
}
const scoreOrder: ElementName[] = ['土', '金', '水', '木', '火']
const generationOrder: ElementName[] = ['木', '火', '土', '金', '水']
const hourFromBirthHour = (birthHour: string) => {
const numericHour = Number.parseInt(birthHour, 10)
if (Number.isFinite(numericHour)) return Math.max(0, Math.min(23, numericHour))
const branchHours: Record<string, number> = {
子: 23, 丑: 1, 寅: 3, 卯: 5, 辰: 7, 巳: 9,
午: 11, 未: 13, 申: 15, 酉: 17, 戌: 19, 亥: 21
}
return branchHours[birthHour.slice(0, 1)] ?? 12
}
export function calculateFiveElements(birthday: string, birthHour: string): FiveElementReport {
const date = new Date(`${birthday}T00:00:00`)
if (Number.isNaN(date.getTime())) {
return {
scores: scoreOrder.map((name) => ({ name, color: elementMeta[name].color, value: 20 })),
favorable: '金水',
unfavorable: '木火土',
dayMaster: '壬'
}
}
const solar = Solar.fromYmdHms(
date.getFullYear(),
date.getMonth() + 1,
date.getDate(),
hourFromBirthHour(birthHour),
0,
0
)
const eightChar = solar.getLunar().getEightChar()
const pillars = [eightChar.getYear(), eightChar.getMonth(), eightChar.getDay(), eightChar.getTime()]
const weights: Record<ElementName, number> = { 木: 0, 火: 0, 土: 0, 金: 0, 水: 0 }
pillars.forEach((pillar) => {
const [stem, branch] = pillar
if (stemElements[stem]) weights[stemElements[stem]] += 2
if (branchElements[branch]) weights[branchElements[branch]] += 1
})
const total = Object.values(weights).reduce((sum, value) => sum + value, 0)
const scores = scoreOrder.map((name) => ({
name,
color: elementMeta[name].color,
value: Math.round((weights[name] / total) * 100)
}))
const dayMaster = eightChar.getDay().slice(0, 1)
const masterElement = stemElements[dayMaster] || '水'
const masterIndex = generationOrder.indexOf(masterElement)
const resourceElement = generationOrder[(masterIndex + generationOrder.length - 1) % generationOrder.length]
const unfavorable = generationOrder.filter((element) => element !== resourceElement && element !== masterElement).join('')
return {
scores,
favorable: `${resourceElement}${masterElement}`,
unfavorable,
dayMaster
}
}