// ============================================================================ // timesync.go — 系统时间同步 // // 从 NTP 服务器读取标准时间,调用平台实现校准系统时钟(Windows 用 SetLocalTime)。 // ============================================================================ package main import ( "encoding/binary" "fmt" "net" "time" "github.com/gen2brain/beeep" ) // ntpEpoch NTP 时间戳起始时间(1900-01-01) var ntpEpoch = time.Date(1900, 1, 1, 0, 0, 0, 0, time.UTC) // queryNTP 向 NTP 服务器查询,返回服务器 UTC 时间 func queryNTP(server string) (time.Time, error) { conn, err := net.DialTimeout("udp", server+":123", 3*time.Second) if err != nil { return time.Time{}, err } defer conn.Close() conn.SetDeadline(time.Now().Add(3 * time.Second)) // 48 字节请求包: LI=0, VN=4, Mode=3(client) req := make([]byte, 48) req[0] = 0x1B if _, err := conn.Write(req); err != nil { return time.Time{}, err } resp := make([]byte, 48) if _, err := conn.Read(resp); err != nil { return time.Time{}, err } secs := binary.BigEndian.Uint32(resp[40:44]) frac := binary.BigEndian.Uint32(resp[44:48]) return ntpEpoch.Add(time.Duration(secs)*time.Second + time.Duration(frac)*1e9/(1<<32)), nil } // syncSystemTime 从 NTP 获取 UTC 时间,按系统当前时区换算后校准本地时钟(校准需管理员权限) func syncSystemTime() error { before := time.Now() fmt.Fprintf(apiLogRing, "[SYS] 开始同步系统时间 | 当前 %s\n", before.Format("2006-01-02 15:04:05.000")) var serverTime time.Time var lastErr error for _, server := range []string{"ntp.aliyun.com", "ntp.tencent.com", "time.windows.com"} { t, err := queryNTP(server) if err == nil { serverTime = t fmt.Fprintf(apiLogRing, "[SYS] NTP 服务器 %s 响应成功\n", server) break } lastErr = err } if serverTime.IsZero() { fmt.Fprintf(apiLogRing, "[SYS] 所有 NTP 服务器均不可达: %v\n", lastErr) return fmt.Errorf("无法连接 NTP 服务器: %v", lastErr) } // 关键:按系统当前时区(如 China Standard Time UTC+8)换算为本地时间 serverLocal := serverTime.In(before.Location()) drift := serverLocal.Sub(before) absDrift := drift if absDrift < 0 { absDrift = -absDrift } if absDrift < time.Second { fmt.Fprintf(apiLogRing, "[SYS] 时钟偏差 %v,无需校准\n", absDrift.Round(time.Millisecond)) _ = beeep.Notify("🕐 时间已精准", fmt.Sprintf("时钟偏差 %v,无需校准", absDrift.Round(time.Millisecond)), "") return nil } if err := setLocalTime(serverLocal); err != nil { fmt.Fprintf(apiLogRing, "[SYS] 校准失败: %v\n", err) return err } after := time.Now() fmt.Fprintf(apiLogRing, "[SYS] 校准成功 | 偏差 %v | %s → %s\n", drift.Round(time.Millisecond), before.Format("15:04:05"), after.Format("15:04:05")) _ = beeep.Notify("🕐 时间同步成功", fmt.Sprintf("偏差: %v\n校准前: %s\n校准后: %s", drift.Round(time.Millisecond), before.Format("15:04:05"), after.Format("15:04:05")), "") return nil }