| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596 |
- // ============================================================================
- // 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
- }
|