很多八字排盘实现都会做「真太阳时」校正,但绝大多数只做了一半——把钟表上的法定时间换算成当地的经度时间,却漏掉了另一半:时差方程(Equation of Time)。这一漏掉的部分全年可达 -14 到 +16 分钟,对以两小时为一柱的时柱来说,足以让一个人的时柱错到隔壁去。本文拆解真太阳时的两步校正,以及为什么第二步最容易被忽略。
真太阳时到底是什么
钟表上的时间是「法定时间」:一个时区里所有人共用同一个标准子午线的时刻。但太阳并不认行政区划。要还原「太阳真正走到你头顶正上方那一刻」,需要在法定时间上做两步修正:
- 经度校正:把标准子午线的时刻换算到你所在经度的地方平时(local mean time)。
- 时差方程(EoT):把「平太阳」的时间修正为「真太阳」的时间。
用一句公式概括:
真太阳时 = 法定时间 + 经度校正 + 时差方程
常见的错误是只做了前两项,把「地方平时」当成了「真太阳时」。
第一步:经度校正(相对好写)
地球自转每小时 15 度,换算下来每 1 度经度差对应 4 分钟。所以从标准子午线到实际出生地的时间修正是:
longitudeCorrection = (longitude - standardMeridian) × 4 # 分钟,每度 4 分钟
这里唯一的坑是 standardMeridian(标准子午线)怎么取。它应该由该地当年的标准时区偏移决定,而标准时区偏移必须剥离夏令时(DST)——夏令时只是把钟拨快了,并不改变太阳的位置。剥离逻辑通常是:
standardOffset = secondsFromGMT(date) - dstOffset(date)
standardMeridian = standardOffset / 3600 × 15
只要 DST 剥离对了,这一步在跨半球、跨夏冬令的各种边界上都能稳定给出正确结果:
| 地点 / 时段 | 是否处于夏令时 | 剥离后标准子午线 |
|---|---|---|
| 悉尼(南半球夏) | 是(AEDT) | 150°E |
| 悉尼(南半球冬) | 否(AEST) | 150°E |
| 纽约(夏) | 是(EDT) | 75°W |
| 伦敦(夏) | 是(BST) | 0° |
注意到悉尼夏冬都收敛到 150°E,纽约夏令时下仍取 75°W,伦敦夏令时下仍取 0°——这正是 DST 被正确剥离的标志。经度校正这一步,数学上并不难,难点在于它给人一种「我已经做完真太阳时」的错觉。
第二步:被漏掉的时差方程
「平太阳」是一个假想的、匀速运行的太阳;而真实的太阳,由于地球轨道是椭圆(近日点跑得快、远日点跑得慢)加上黄赤交角的存在,并不匀速。真太阳与平太阳之间的时间差,就是时差方程。
它是一个只跟「一年中的第几天」有关的周期量,全年在约 -14 到 +16 分钟之间起伏:
| 日期 | 时差方程(EoT) |
|---|---|
| 2 月 11 日前后 | 约 -14.6 分钟 |
| 5 月中旬 | 约 +3.8 分钟 |
| 7 月下旬 | 约 -6.2 分钟 |
| 11 月初 | 约 +16.4 分钟 |
| 12 月下旬 | 约 -0.5 分钟 |
一个常用的近似公式(以 N 为一年中的第几天)是:
B = 2π × (N - 81) / 365
EoT ≈ 9.87·sin(2B) - 7.53·cos(B) - 1.5·sin(B) # 单位:分钟
这个近似对于命理排盘的精度需求已经足够。若追求天文级精度,可换用更高阶的展开式,但对本文讨论的量级没有实质影响。
为什么它对八字时柱是「硬伤」
八字的时柱以两小时为一界(子时、丑时……,现代常以 23:00 / 01:00 / 03:00…… 为界)。这意味着任何一个把出生时刻推近或推离整点边界的误差,都可能改变时柱的落点。
而时差方程的量级是 ±16 分钟——单独一项就足以跨界。举个不指向任何真实个人的例子:某人出生在午时前后、钟表时间落在 11:40 到 12:20 这个区间,如果排盘只做经度校正、忽略 EoT,那么在一年中 EoT 偏大的月份里,校正后的真太阳时可能从「午初」滑到「巳末」,或者反过来——时柱直接错一柱,后续所有以时柱为基础的推演随之改变。
本文只讨论历法与天文时间的换算方法。八字属于传统术数文化,相关推演仅为文化研究,并非命运预测或任何医疗、婚姻、灾祸方面的建议。
三个容易踩的工程坑
1. 用「当前时间」还是「出生时间」算时区
一个常见写法是拿 now()(程序运行的当下)去查时区偏移,而不是拿出生日期去查。大多数时候这「碰巧」是对的,因为剥离 DST 之后标准子午线通常不随年份变化。但一旦某地的历史时区规则发生过变动,用当下时间就会算错。正确做法是永远拿出生日期去查询当年的时区与夏令时状态。
2. 历史夏令时边界
中国曾在 1986 至 1991 年实行过夏令时(每年 4 月至 9 月拨快一小时)。操作系统的时区数据库对这段历史的识别,在起止边界上未必与官方公告逐日吻合。对这一时段、且出生在 5 至 9 月的记录,稳妥做法是在界面上提示用户「此时段实施过夏令时,请确认输入的是钟表时间」,而不是默默按某一种解释处理。
3. 经度数据库不全会「静默出错」
如果排盘依赖一个内置的城市经度表,而这个表只覆盖了少数大城市,那么表外的地点往往会 fallback 成「不校正」。这种错误最危险的地方在于它不报错、不提示,用户完全无从察觉。要么把经度库做全(至少覆盖主要人口城市),要么允许直接输入经纬度,并对 fallback 的情况给出明确提示。
修正后的完整算法
把两步校正合起来,一个语言无关的实现骨架是这样的:
function trueSolarTimeOffset(longitude, timeZone, birthDate):
tzOffset = secondsFromGMT(timeZone, birthDate)
- dstOffset(timeZone, birthDate) # 剥离夏令时
standardMeridian = tzOffset / 3600 × 15
longitudeCorrection = (longitude - standardMeridian) × 4 # 每度 4 分钟
return longitudeCorrection + equationOfTime(birthDate)
关键改动只有一处:函数签名必须带上出生日期这个参数——EoT 依赖它,历史时区查询也依赖它。这个改动会沿调用链一路传导,所有拿到出生时刻的地方都要把日期透传进来。看似只是加了一个参数,实则是把「真太阳时」从半成品补成了完整实现。
小结
真太阳时的两步校正里,经度校正直白、容易写对,反而成了误导——它让人以为工作已经完成。真正决定成败的是那条只有十几分钟、却足以跨越时柱边界的时差方程。对任何以「出生时刻」为根基的排盘系统而言,漏掉 EoT 不是精度问题,而是可能改变结论的结构性缺陷。