044f2f4381
- 新增半解析、旧模型、自定义与多源堆叠星历实例,提供 Context、Instance、SPK 读取及状态与视位置计算 - 统一太阳、月球和行星实例接口,扩展黄经穿越、留、相位、最近角距、偕日可见、任意高度升落、轨道节点及凌日等天象查询 - 完善日月食、月掩与凌日的月缘剖面、有限盘面接触、站心修正、局地可见性、全球路径和地图输出 - 新增 SVG、GeoJSON、KML 天象输出及凌日、月掩和月缘地形相关缓存与几何求解 - 新增恒星星表导入与查询,支持 CSV、Hipparcos、BSC5、Stellarium 等格式及名称、交叉标识、锥面检索和星等过滤 - 补充 IAU 2006、地球定向、折射消光、大气观测、轨道传播及半解析行星/月球数据支持 - 将默认未来民用时标切换为 TimeScaleLeapHour,更新 2027–2400 农历速查表、中文日期解析和时区边界处理 - 增加事件基线、跨模型回归、几何边界、月缘地形和输出契约测试,完善中英文手册、示例资源、NOTICE 与忽略规则
121 lines
3.3 KiB
Go
121 lines
3.3 KiB
Go
// Package timenote 生成出图时写入图内或图注的时标声明。
|
||
package timenote
|
||
|
||
import (
|
||
"fmt"
|
||
"strconv"
|
||
"strings"
|
||
"time"
|
||
|
||
"b612.me/astro/basic"
|
||
)
|
||
|
||
const english = "en"
|
||
|
||
// Scale 生成图内时标声明:UTC 口径写显示时区与 UTC 偏移,UT1 口径补 DUT1 差值 / builds the in-figure time-scale declaration.
|
||
func Scale(scale basic.TimeScale, instant time.Time, location *time.Location, language string) string {
|
||
if scale == basic.TimeScaleUT1 {
|
||
dut1 := basic.DUT1(instant)
|
||
if language == english {
|
||
return fmt.Sprintf("Times are UT1 (Universal Time 1); DUT1 = UT1−UTC = %+.2f s.", dut1)
|
||
}
|
||
return fmt.Sprintf("图中时刻为 UT1(世界时),DUT1 = UT1−UTC = %+.2f s。", dut1)
|
||
}
|
||
zone, offset := displayZone(instant, location)
|
||
if offset == 0 {
|
||
if language == english {
|
||
return "All times are UTC"
|
||
}
|
||
return "图中时刻为 UTC"
|
||
}
|
||
if named, ok := namedZoneOffset(zone); ok && named == offset {
|
||
// 时区名本身就是偏移标签(FixedZone("UTC+08:00")、"Etc/GMT+8" 之类)时再写一遍偏移就是重复文案。
|
||
if language == english {
|
||
return fmt.Sprintf("All times are UTC (shown in %s)", zone)
|
||
}
|
||
return fmt.Sprintf("图中时刻为 UTC(显示时区 %s)", zone)
|
||
}
|
||
sign := "+"
|
||
if offset < 0 {
|
||
sign, offset = "-", -offset
|
||
}
|
||
hours, minutes := offset/3600, offset%3600/60
|
||
if language == english {
|
||
return fmt.Sprintf("All times are UTC (shown in %s, UTC%s%02d:%02d)", zone, sign, hours, minutes)
|
||
}
|
||
return fmt.Sprintf("图中时刻为 UTC(显示时区 %s,UTC%s%02d:%02d)", zone, sign, hours, minutes)
|
||
}
|
||
|
||
// Merge 把时标声明并进图注,已含声明时不重复 / appends the declaration to a footer note once.
|
||
func Merge(text, note string) string {
|
||
if note == "" {
|
||
return text
|
||
}
|
||
if text == "" {
|
||
return note
|
||
}
|
||
if strings.Contains(text, note) {
|
||
return text
|
||
}
|
||
return text + " " + note
|
||
}
|
||
|
||
// namedZoneOffset 解析时区名里写死的 UTC 偏移,秒为单位;名字不含偏移时返回 false。
|
||
func namedZoneOffset(name string) (int, bool) {
|
||
rest := strings.ToUpper(name)
|
||
for _, prefix := range []string{"UTC", "GMT", "UT"} {
|
||
if strings.HasPrefix(rest, prefix) {
|
||
rest = rest[len(prefix):]
|
||
break
|
||
}
|
||
}
|
||
if rest == "" {
|
||
return 0, false
|
||
}
|
||
sign := 1
|
||
switch rest[0] {
|
||
case '+':
|
||
rest = rest[1:]
|
||
case '-':
|
||
sign, rest = -1, rest[1:]
|
||
default:
|
||
return 0, false
|
||
}
|
||
digits := strings.ReplaceAll(rest, ":", "")
|
||
if digits == "" || len(digits) > 6 {
|
||
return 0, false
|
||
}
|
||
for _, symbol := range digits {
|
||
if symbol < '0' || symbol > '9' {
|
||
return 0, false
|
||
}
|
||
}
|
||
hours, minutes, seconds := 0, 0, 0
|
||
switch len(digits) {
|
||
case 1, 2:
|
||
hours, _ = strconv.Atoi(digits)
|
||
case 3, 4:
|
||
hours, _ = strconv.Atoi(digits[:len(digits)-2])
|
||
minutes, _ = strconv.Atoi(digits[len(digits)-2:])
|
||
case 5, 6:
|
||
hours, _ = strconv.Atoi(digits[:len(digits)-4])
|
||
minutes, _ = strconv.Atoi(digits[len(digits)-4 : len(digits)-2])
|
||
seconds, _ = strconv.Atoi(digits[len(digits)-2:])
|
||
}
|
||
if minutes > 59 || seconds > 59 {
|
||
return 0, false
|
||
}
|
||
return sign * (hours*3600 + minutes*60 + seconds), true
|
||
}
|
||
|
||
func displayZone(instant time.Time, location *time.Location) (string, int) {
|
||
if location != nil {
|
||
instant = instant.In(location)
|
||
}
|
||
zone, offset := instant.Zone()
|
||
if zone == "" {
|
||
zone = "UTC"
|
||
}
|
||
return zone, offset
|
||
}
|