- 新增 ADR 文档记录生辰信息存储设计决策 - 新增真太阳时计算模块 (solarTime.ts),基于 Spencer 1971 时差方程 - 新增城市经纬度内置数据库,覆盖全国主要区县,系统自动匹配坐标 - 更新紫微页面:移除手动经纬度输入,改为 500ms 防抖自动定位 - 更新所有 9 个语言文件,替换经纬度提示为定位提示
117 lines
3.7 KiB
TypeScript
117 lines
3.7 KiB
TypeScript
/**
|
||
* 真太阳时计算模块
|
||
*
|
||
* 真太阳时 = 平太阳时 + 经度修正 + 时差方程修正
|
||
* - 经度修正:经度每差 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
|
||
} |