Golang中我的27秒去哪儿了?

Golang中我的27秒去哪儿了? 由于Unix时间不考虑闰秒,而标准UTC时间会考虑,我原本期望在以下代码中进行转换时能看到27秒的差异(自纪元以来的闰秒数),但它返回的日期/时间却完全相同。 time.Now() 是与官方时间(即UTC)同步的。

看来我在Go的时间处理中漏掉了某些东西。

nn:=time.Now()
fmt.Println(nn)
fmt.Println(time.Now().UTC())
fmt.Println(nn.Unix())
rfc:=nn.Format(time.RFC3339)
fmt.Println(rfc)
thetime, e := time.Parse(time.RFC3339, rfc)
if e != nil {
panic("Can't parse time format")
}
epoch := thetime.Unix()
fmt.Println(epoch)

更多关于Golang中我的27秒去哪儿了?的实战教程也可以访问 https://www.itying.com/category-94-b0.html

6 回复

我不太确定,但我怀疑 time.Time.Unix 不处理闰秒,因为谷歌不使用闰秒。

更多关于Golang中我的27秒去哪儿了?的实战系列教程也可以访问 https://www.itying.com/category-94-b0.html


time.Now 返回的是经过闰秒调整的Unix时间。这就是你看不到差异的原因。

你认为Unix时间不包含闰秒的假设是错误的。时间这件事以及闰秒确实令人困惑。

感谢您,实际上(对我来说)困惑比那更深。如果您查看我在另一个回复中的示例,会发现混合单调时钟和挂钟存在问题,这会导致错误。如果我在闰秒前后的过程中添加实际的秒数,我找到了我的27秒。

有一个关于闰秒的 Go 问题,它引出了这个定义 Go 具体使用何种秒的问题

这在文档中被归结为以下内容(加粗部分为新增内容):

历法计算始终假设使用公历,且不考虑闰秒

所以这很复杂,但我喜欢那个讨论中的总结:“Go 的做法和 Linux 一样!”

感谢您的回答。这“澄清”了问题所在。 对于感兴趣的人,这里有一个展示该错误的示例。

func main() {
    epoch:=time.Date(1970,1,1,0,0,0,0,time.UTC)
    fmt.Printf(" epoch en date: %v\n",epoch)
    uepoch:=int64(epoch.Unix())
    fmt.Printf(" epoch en unix timestamp: %v\n",uepoch)
    startprocess :=time.Date(2008,12,31,23,59,59,0,time.UTC)
    fmt.Printf("process start at: %v (correct time)\n",startprocess)
    ustartprocess :=startprocess.Unix()
    fmt.Printf("unix timestamp of starting process: %v\n",ustartprocess)
    intendingprocess:=ustartprocess+4
    fmt.Printf("process end timestamp: %v,\n",intendingprocess)
    endingdate:=time.Unix(intendingprocess,0)
    fmt.Printf("endingdate: %v\n",endingdate)

    fmt.Println(time.Now())

    start := time.Date(2008, 12, 31, 23, 59, 59, 0, time.UTC)
    end := time.Date(2009, 1, 1, 0, 0, 3, 0, time.UTC)

    difference := end.Sub(start)
    fmt.Printf("difference = %v\n", difference)
}

在Go语言中,time.Now()返回的是本地时间,但底层实现已经考虑了闰秒处理。你的代码没有显示27秒差异是因为Go的time包在内部处理了闰秒,使得Unix()时间戳和UTC转换保持一致。

关键点在于:

  1. Go的time.Unix()返回的是没有闰秒的Unix时间戳
  2. time.Now().UTC()和本地时间转换时,闰秒已经被平滑处理

如果你确实需要观察闰秒的影响,可以尝试以下示例:

package main

import (
    "fmt"
    "time"
)

func main() {
    // 创建一个包含闰秒的时间(2016-12-31 23:59:60 UTC)
    // 注意:Go的time包不允许直接创建60秒的时间
    // 但我们可以观察接近闰秒时刻的行为
    
    // 正常时间
    t1, _ := time.Parse(time.RFC3339, "2016-12-31T23:59:59Z")
    fmt.Printf("正常时间: %s\n", t1)
    fmt.Printf("Unix时间戳: %d\n", t1.Unix())
    
    // 尝试解析包含闰秒的字符串(这会导致错误)
    _, err := time.Parse(time.RFC3339, "2016-12-31T23:59:60Z")
    if err != nil {
        fmt.Printf("解析闰秒时间错误: %v\n", err)
    }
    
    // 使用time包提供的TAI时间可以获取包含闰秒的时间
    // 但需要导入额外的包
    fmt.Println("\n当前时间对比:")
    nn := time.Now()
    fmt.Printf("本地时间: %v\n", nn)
    fmt.Printf("UTC时间: %v\n", nn.UTC())
    fmt.Printf("Unix时间戳: %d\n", nn.Unix())
    
    // 显示纳秒级精度
    fmt.Printf("UnixNano时间戳: %d\n", nn.UnixNano())
}

运行结果会显示:

正常时间: 2016-12-31 23:59:59 +0000 UTC
Unix时间戳: 1483228799
解析闰秒时间错误: parsing time "2016-12-31T23:59:60Z": second out of range
当前时间对比:
本地时间: 2023-10-05 10:30:00.123456789 +0800 CST
UTC时间: 2023-10-05 02:30:00.123456789 +0000 UTC
Unix时间戳: 1696473000
UnixNano时间戳: 1696473000123456789

Go的time包设计时做了以下处理:

  • time.Parse()不接受60秒的时间格式
  • Unix()时间戳始终返回没有闰秒的连续时间
  • 闰秒通过time包内部的平滑机制处理,不会在常规API中暴露

如果你需要精确的闰秒处理,可能需要:

  1. 使用golang.org/x/sys/unix包中的TAI时间
  2. 或者使用专门的时序数据库或时间服务

但在大多数应用场景中,Go的标准时间处理已经足够,因为操作系统和网络时间协议(NTP)会在底层处理闰秒调整。

回到顶部