/** * 真太阳时计算模块 * * 真太阳时 = 平太阳时 + 经度修正 + 时差方程修正 * - 经度修正:经度每差 1°,时间差 4 分钟 * - 时差方程:基于地球公转轨道离心率和黄赤交角的天文修正 * * @example * ```typescript * const result = calculateTrueSolarTime({ * date: '2026-08-13', * hour: 14, * minute: 30, * longitude: 116.4, * timezoneOffset: 8, * }) * // result.trueSolarTime => '2026-08-13T14:24:30' * // result.correctionMinutes => -5.5 * ``` */ export interface TrueSolarTimeParams { /** 公历日期 YYYY-MM-DD */ date: string /** 小时 (0-23) */ hour: number /** 分钟 (0-59) */ minute: number /** 出生地经度(东经为正,西经为负) */ longitude: number /** 时区偏移小时数(东八区为 8,东七区为 7) */ timezoneOffset: number } export interface TrueSolarTimeResult { /** 真太阳时字符串 HH:mm:ss */ trueSolarTime: string /** 总修正分钟数(正数=提前,负数=推迟) */ correctionMinutes: number /** 经度修正分钟数 */ longitudeCorrection: number /** 时差方程修正分钟数 */ equationOfTime: number } /** * 计算真太阳时 * * 算法来源: * - 经度修正:时区中央经线与本地经度的差值 × 4 分钟/度 * - 中国标准时间 (UTC+8) 中央经线为 120°E * - 时差方程:基于 Spencer (1971) 的近似公式 * - 参考:https://www.esrl.noaa.gov/gmd/grad/solcalc/ */ export function calculateTrueSolarTime(params: TrueSolarTimeParams): TrueSolarTimeResult { const { date, hour, minute, longitude, timezoneOffset } = params // 1. 经度修正 const standardMeridian = timezoneOffset * 15 const longitudeCorrection = (longitude - standardMeridian) * 4 // 2. 时差方程修正(基于 Spencer 1971 近似公式) const dateObj = new Date(`${date}T00:00:00`) const dayOfYear = getDayOfYear(dateObj) const fractionalYear = (2 * Math.PI / 365) * (dayOfYear - 1 + (hour - 12) / 24) const equationOfTime = calculateEquationOfTime(fractionalYear) // 3. 总修正 const totalCorrection = longitudeCorrection + equationOfTime // 4. 计算真太阳时 const totalMinutes = hour * 60 + minute + totalCorrection const adjustedHours = Math.floor(totalMinutes / 60) % 24 const adjustedMinutes = Math.floor(totalMinutes % 60) const adjustedSeconds = Math.round((totalMinutes % 1) * 60) const trueSolarTime = `${String(adjustedHours).padStart(2, '0')}:${String(adjustedMinutes).padStart(2, '0')}:${String(adjustedSeconds).padStart(2, '0')}` return { trueSolarTime, correctionMinutes: Math.round(totalCorrection * 100) / 100, longitudeCorrection: Math.round(longitudeCorrection * 100) / 100, equationOfTime: Math.round(equationOfTime * 100) / 100, } } /** * 计算一年中的第几天(1 月 1 日为第 1 天) */ function getDayOfYear(date: Date): number { const start = new Date(date.getFullYear(), 0, 0) const diff = date.getTime() - start.getTime() return Math.floor(diff / (1000 * 60 * 60 * 24)) } /** * 计算时差方程(分钟) * * 基于 Spencer (1971) 傅里叶级数近似: * EoT = 229.2 × (0.000075 + 0.001868×cos(γ) - 0.032077×sin(γ) * - 0.014615×cos(2γ) - 0.04089×sin(2γ)) * * 其中 γ = 2π/365 × (N - 1 + (t - 12) / 24),N 为一年中的第几天 * * 结果范围约为 -16 到 +14 分钟 */ function calculateEquationOfTime(fractionalYear: number): number { const coeff = 229.2 const eot = coeff * ( 0.000075 + 0.001868 * Math.cos(fractionalYear) - 0.032077 * Math.sin(fractionalYear) - 0.014615 * Math.cos(2 * fractionalYear) - 0.04089 * Math.sin(2 * fractionalYear) ) return eot }