Files
everything-is-suitable/everything-is-suitable-uniapp/src/algorithms/solarTime.ts
T
zhangxiang 34e9c3721d feat: 实现生辰信息存储与真太阳时计算
- 新增 ADR 文档记录生辰信息存储设计决策
- 新增真太阳时计算模块 (solarTime.ts),基于 Spencer 1971 时差方程
- 新增城市经纬度内置数据库,覆盖全国主要区县,系统自动匹配坐标
- 更新紫微页面:移除手动经纬度输入,改为 500ms 防抖自动定位
- 更新所有 9 个语言文件,替换经纬度提示为定位提示
2026-08-12 20:44:45 +08:00

117 lines
3.7 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
/**
* 真太阳时计算模块
*
* 真太阳时 = 平太阳时 + 经度修正 + 时差方程修正
* - 经度修正:经度每差 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
}