Files
astro/doc/manual/timescale.md
T
b612 16c62a97d5 feat: 完善时标与天象几何计算并扩展输出接口
- 新增时标、ΔT 模型、质心时间与 UT1 支持
- 改进日月食、月掩、行星事件及路径边界计算
- 完善恒星三维自行与动态距离传播
- 扩展 SVG、GeoJSON、KML 输出与底层距离换算工具
- 整理中英文手册、示例资源及回归测试
2026-09-23 18:55:12 +08:00

12 KiB
Raw Permalink Blame History

时标

English | README

同一个物理时刻可以用 UTC、UT1 或 TT 表示。UTC(协调世界时)用于民用时间,UT1(世界时)反映地球自转,TT 用于星历计算。它们之间的两个差值是 ΔT = TT − UT1 和 DUT1 = UT1 − UTC,单位都是秒。

目录

时间参数

一般观测接口接收表示民用时刻的 time.Time,先取其 UTC 值,再按计算需要转换到 UT1 或 TT。改变 Location 只改变同一时刻的显示方式;日出日落等按日搜索的接口还会用它确定当地日期。

本库约定:1972-01-01 以前的民用时间按 UT1 处理;这是库的历算约定,不表示当时不存在 UTC。1972 年以后,TT−UTC 由内置闰秒表、所选政策或显式覆盖决定,ΔT 模型负责 TT−UT1。

以下输入需要另外区分:

输入 解释
astro.TTFromUTC / UT1FromUTC 的参数 民用时刻,可带任意时区
astro.UTCFromTT / UTCFromUT1 的参数 TT / UT1 读数,以 UTC Location 承载
astro.TCGFromTT / TCBFromTT / TDBFromTT 的参数 TT 读数,不是民用 UTC 时刻
orbit.Elements.EpochJD / TpJD TT/TDB 儒略日,见轨道手册
basic 的 JD 数值接口 由函数名和参数契约指定时标,数值本身不记录时标
calendar.Date2JD / basic.Date2JD 读取年月日和钟面字段,不自动转换时区;需要 UTC JD 时先传入 date.UTC()
公农历转换 按历法手册解释日期,默认使用北京时间

time.Time 不能表示闰秒的 23:59:60。历史日期还须考虑本库的儒略历/格里高利历切换与 Go 前推格里高利历之间的差别。

time.Time 换算 API

这些接口位于根包 b612.me/astro。

接口 返回值
TTFromUTC(date) 同一时刻的 TT 读数
UTCFromTT(tt) TT 读数对应的UTC时刻
UT1FromUTC(date) 同一时刻的 UT1 读数
UTCFromUT1(ut1) UT1 读数对应的UTC时刻
DUT1(date) UT1−UTC,秒
LabelIn(scale, date) TimeScaleUTC 保留输入;TimeScaleUT1 返回 UT1 读数
TCGFromTT(tt) / TTFromTCG(tcg) TT 与地心坐标时 TCG 互换
TCBFromTT(tt) / TTFromTCB(tcb) TT 与太阳系质心坐标时 TCB 互换,采用地心近似
TDBFromTT(tt) / TTFromTDB(tdb) TT 与太阳系质心力学时 TDB 互换,采用地心近似
TCBFromTDB(tdb) / TDBFromTCB(tcb) TDB 与 TCB 的线性互换
TCGMinusTT(tt) / TCBMinusTT(tt) / TDBMinusTT(tt) 相应时标与 TT 的秒差,输入为 TT 读数

TT、UT1、TCG、TCB、TDB 的换算结果虽然使用 time.Time 类型和 UTC Location,但字段表示的是相应时标的读数。不能把这个值直接传回要求民用时刻的太阳、月亮等接口,否则相当于再次移动了计算时刻。需要还原时,调用对应逆变换。

package main

import (
	"fmt"
	"time"

	"b612.me/astro"
)

func main() {
	date := time.Date(2026, 4, 1, 0, 0, 0, 0, time.UTC)
	tt := astro.TTFromUTC(date)
	ut1 := astro.UT1FromUTC(date)
	fmt.Println("UTC:", date.Format(time.RFC3339Nano))
	fmt.Println("TT:", tt.Format("2006-01-02 15:04:05.000000"))
	fmt.Println("UT1:", ut1.Format("2006-01-02 15:04:05.000000"))
	fmt.Printf("DUT1: %.6f s\n", astro.DUT1(date))
	fmt.Println("UTC from TT:", astro.UTCFromTT(tt).Format(time.RFC3339Nano))
}

数值换算经过浮点儒略日,往返结果可能有微小舍入差。TT/UT1 的显示格式不加 Z,以免被当成 UTC 时间戳。

儒略日换算 API

下列函数位于 basic,参数与返回值是 float64。换算函数返回儒略日,差值函数返回秒。

接口 输入 → 输出 说明
UTC2TT UTC JD → TT JD 1972 年前按 UT1,之后使用 TT−UTC 模型
TT2UTC TT JD → UTC JD 上述逆变换
UT12TT UT1 JD → TT JD 使用当前 ΔT 模型
TT2UT1 TT JD → UT1 JD 上述逆变换
UTC2UT1 UTC JD → UT1 JD 等于 TT2UT1(UTC2TT(jd))
UT12UTC UT1 JD → UTC JD 上述逆变换
TTMinusUTCSeconds UTC JD → 秒 内置闰秒段为 32.184 + (TAI−UTC);也可由注入函数覆盖
DUT1Seconds UTC JD → 秒 (TT−UTC) − ΔT
DeltaT JD 或十进制年 → 秒 第二参数为 true 时输入是 UT 儒略日,为 false 时是十进制年
TT2TCG / TCG2TT TT JD ↔ TCG JD 线性换算
TT2TCB / TCB2TT TT JD ↔ TCB JD 地心近似
TT2TDB / TDB2TT TT JD ↔ TDB JD 地心近似
TCB2TDB / TDB2TCB TCB JD ↔ TDB JD 线性换算,含 TDB0 常数
TCGMinusTTSeconds / TCBMinusTTSeconds / TDBMinusTTSeconds TT JD → 秒 相应时标与 TT 的秒差

闰秒或模拟闰时带来阶跃时,TT→民用时间的逆变换存在与阶跃宽度相同的不可表示区间;不能要求跨该区间的逐值往返恒等。

TTMinusUTCSeconds 与 DefaultTTMinusUTC() 读取闰秒表值(前者可被注入函数覆盖),不应用 UTC2TT 的未来政策外推。换算未来民用时刻时直接使用 UTC2TT 或 TTFromUTC。

TCG、TCB 与 TDB

TCG 是地心坐标时;TCB 与 TDB 分别是太阳系质心坐标时和太阳系质心力学时。TT 与 TCG、TCB 与 TDB 的关系为线性定义;TT 与 TDB 的关系在这里采用地心近似,不含观测者位置引起的日周项。

令 T0 = 2443144.5003725、LG = 6.969290134e-10、LB = 1.550519768e-8、TDB0 = −65.5e-6 秒,时标读数用 JD 表示:

TCG − TT = LG / (1 − LG) × (TT − T0)
TDB = TCB − LB × (TCB − T0) + TDB0 / 86400
TCB − TT = [LB × (TT − T0) + (TDB − TT) − TDB0 / 86400] / (1 − LB)

参考历元 T0 不表示四个时标的读数在此全部相等。现代日期 TDB−TT 的主要年周期振幅约 1.7 ms;TCG−TT 每年增加约 0.022 秒,TCB−TT 每年增加约 0.489 秒。

TDB−TT 使用 40 项截断级数和质量调整项。在 −3000 至 +6000 年范围,相对完整 787 项地心级数,遗漏项绝对幅度之和给出的保守截断误差上界为 11 µs;每 31 天抽样的最大差约 2.0 µs。抽样最大差不是全时域误差保证,截断误差也不包含完整模型自身的误差;范围外不作精度承诺。

单个 float64 JD 在现代日期附近的分辨率约为 40 µs,time.Time 包装同样经过这一步舍入。比较微秒级时标差值应使用 *MinusTT 或 *MinusTTSeconds,不要把两个完整 JD 相减后再换算成秒。

ΔT 模型

默认模型为 SMH2016 与 Morrison 2021 的样条和长期外推,并在实测覆盖期内优先使用逐月 ΔT 表。当前表延伸到 2026 年 9 月 1 日,末点为快速观测值,尚非最终解;表外在端点作常值锚定以保持连续。DeltaT 对超出 ±40000 年的输入返回 NaN。

常量 模型
DeltaTModelDefault / DeltaTModelSMH2016 内置默认模型
DeltaTModelMS2004 Morrison & Stephenson 2004,ΔT = −20 + 32u²,u = (year−1820)/100
DeltaTModelEspenakMeeus2006 Espenak & Meeus 2006 分段多项式
DeltaTModelNASACanon2006 上述多项式加 NASA 目录配对项:1955 年以前加 −0.000012932(year−1955)²
DeltaTModelManual 查询状态时表示当前使用任意注入函数,不能作为命名模型安装

SetDeltaTModel(model, keepObserved) 设置进程级模型,返回是否成功;未知模型不会改变当前状态。keepObserved=true 保留实测段,模型只用于表外;false 则全程使用所选模型。GetDeltaTModel() 返回模型标识和该开关。

比较不同模型

DeltaTModelSeconds 不改变进程状态,适合比较同一时刻的不同 ΔT 取值:

package main

import (
	"fmt"
	"time"

	"b612.me/astro"
	"b612.me/astro/basic"
)

func main() {
	date := time.Date(2100, 1, 1, 0, 0, 0, 0, time.UTC)
	jd := basic.Date2JD(date)
	for _, model := range []astro.DeltaTModel{
		astro.DeltaTModelSMH2016,
		astro.DeltaTModelEspenakMeeus2006,
		astro.DeltaTModelNASACanon2006,
	} {
		fmt.Println(model, astro.DeltaTModelSeconds(model, jd, false))
	}
}

ΔT 的未来值不能精确预知。按现有模型计算,Espenak–Meeus 2006 与默认模型在 2035、2050、2100、2200 年的差异约为 11、21、116、275 秒。远期日月食的时刻和地面路径会受此影响;与日月食目录对照时,应先统一 ΔT 模型。

接入外部模型

根包接口 用途
DeltaT() / SetDeltaT(fn) 读取、设置 ΔT 函数;函数类型为 func(float64, bool) float64
DefaultDeltaT() 取得内置默认 ΔT 函数
TTMinusUTC() / SetTTMinusUTC(fn) 读取、设置 TT−UTC 覆盖;函数类型为 func(float64) float64,参数是民用 JD
DefaultTTMinusUTC() 取得内置闰秒表函数,不读取注入覆盖或未来政策

两个回调均返回秒。SetDeltaT(nil) 恢复默认 ΔT;SetTTMinusUTC(nil) 恢复内置闰秒表与未来政策。未设置 TT−UTC 覆盖时,TTMinusUTC() 返回 nil。

basic 中对应的接口是 GetDeltaTFn / SetDeltaTFn、GetTTMinusUTCFn / SetTTMinusUTCFn,与根包共享状态。TT−UTC 覆盖作用于 1972 年以后的查询,优先于未来政策;1972 年前仍采用本库的 UT1 约定。

这些设置会影响同一进程内的后续计算。应用应在开始计算前设定;临时对照结束后恢复原来的函数或命名模型,不要把模型切换当成某一次函数调用的局部选项。

未来的 UTC 对齐口径假设

SetTimeScaleFuturePolicy 与 GetTimeScaleFuturePolicy 选择民用时标换算政策,默认是 TimeScaleLeapSecond。除 TimeScaleUT1Civil 替换全时轴外,其余政策只控制实测窗口之后的 TT−UTC;1972 年后显式注入的 TT−UTC 覆盖始终优先。它与出图的 TimeScaleUTC / TimeScaleUT1 选项用途不同。

政策 计算假设
TimeScaleLeapSecond(零值、默认) 从末端 TT−UTC 出发,以最少整数秒校正使外推 DUT1 回到 ±0.9 秒内
TimeScaleAssumeUT1Tracking 保留末端 DUT1,TT−UTC 随外推 ΔT 平滑变化
TimeScaleFreezeUTCOffset TT−UTC 固定在内置窗口末端值,当前为 69.184 秒
TimeScaleLeapHour 按 ΔT 外推,以最少整小时校正使 DUT1 回到 ±3600 秒内,用于情景计算
TimeScaleUT1Civil 全时轴将民用时间视为 UT1,含历史闰秒表覆盖期;无显式覆盖时 DUT1 恒为 0

这些选项是计算假设,不是对未来闰秒或国际决议的预报。整数秒或小时校正由当前查询时刻的 ΔT 决定,不记录历次校正,也不强制落在公告的日历边界。阶跃附近不保证逐值往返恒等;步进政策依赖的 ΔT 无效或超出模型范围时返回 NaN,不会退回正常偏移。

默认模型也不能保证任意未来日期相对真实 UTC 的误差小于 0.9 秒。需要精确民用时刻时,应使用对应日期的已发布时标数据。

SVG、GeoJSON 与 KML 的时间标签

SVG 和 GeoJSON 默认输出民用时间标签。需要 UT1 时,可使用 ...InUT1 结果转换函数,或设置导出选项的 TimeScale: astro.TimeScaleUT1。UT1 输出的 Location 应为 nil 或 time.UTC。

已有几何换时标时仍对应同一个物理时刻。时间标记若按输出时标的整刻度重新取点,采样时刻会随之改变,标记位置也会移动。GeoJSON 用 time_scale 标明标签口径;KML 的 <when> 必须是 UTC,转换器会把 UT1 标签按当前模型换回 UTC,并保留原 time_scale 属性。生成 GeoJSON 和转成 KML 时应使用一致的时标模型。

具体选项、时间标记步长和 KML 时间轴行为见地图与数据导出。