import { Solar } from 'lunar-typescript'; import { createChart } from './chart'; import type { BaziChart, CivilTime, DayBoundary, FourPillarsInput, TraceStep, } from './types'; export function normalizeCivilTime(civilTime: CivilTime): Required { const normalized = { year: civilTime.year, month: civilTime.month, day: civilTime.day, hour: civilTime.hour ?? 0, minute: civilTime.minute ?? 0, second: civilTime.second ?? 0, }; for (const [key, value] of Object.entries(normalized)) { if (!Number.isInteger(value)) { throw new TypeError(`civilTime.${key} 必须是整数;收到:${value}`); } } if (normalized.year < 1 || normalized.year > 9999) { throw new RangeError('civilTime.year 必须在 1 到 9999 之间'); } if (normalized.hour < 0 || normalized.hour > 23) { throw new RangeError('civilTime.hour 必须在 0 到 23 之间'); } if (normalized.minute < 0 || normalized.minute > 59) { throw new RangeError('civilTime.minute 必须在 0 到 59 之间'); } if (normalized.second < 0 || normalized.second > 59) { throw new RangeError('civilTime.second 必须在 0 到 59 之间'); } // Date.UTC 会把 0..99 年解释为 1900..1999,先创建基准日期再显式设置年份。 const check = new Date(0); check.setUTCFullYear(normalized.year, normalized.month - 1, normalized.day); check.setUTCHours( normalized.hour, normalized.minute, normalized.second, 0, ); if ( check.getUTCFullYear() !== normalized.year || check.getUTCMonth() !== normalized.month - 1 || check.getUTCDate() !== normalized.day ) { throw new RangeError('civilTime 中的公历日期无效'); } return normalized; } export function chartFromCivilTime( civilTime: Required, dayBoundary: DayBoundary, ): { chart: BaziChart; pillars: FourPillarsInput; trace: TraceStep[] } { const solar = Solar.fromYmdHms( civilTime.year, civilTime.month, civilTime.day, civilTime.hour, civilTime.minute, civilTime.second, ); const eightChar = solar.getLunar().getEightChar(); // lunar-typescript: sect 1 为 23:00 换日,sect 2 为 00:00 换日。 eightChar.setSect(dayBoundary === 'zi-hour' ? 1 : 2); const pillars = { year: eightChar.getYear(), month: eightChar.getMonth(), day: eightChar.getDay(), hour: eightChar.getTime(), }; return { chart: createChart(pillars), pillars, trace: [ { ruleId: 'calendar.solar-input', description: '将中国标准时间的公历民用时间交给历法核心。', input: { civilTime }, output: { solar: solar.toYmdHms() }, }, { ruleId: 'calendar.four-pillars', description: '年柱以立春换年,月柱以节换月,并按配置应用换日规则。', input: { dayBoundary }, output: { pillars }, }, ], }; }