七叶笔记 » golang编程 » Go源代码中的那些秘密:time.minWall是1885?

Go源代码中的那些秘密:time.minWall是1885?

前言

前段时间,我在某个 Slack 工作区与朋友聊天:

我那么说基本是在开玩笑,因为我也不知道为什么将其设置为 1885 年。尽管其背后的事实与我在 Go 中的日常编码没有任何关系,但我还是情不自禁地询问了幕后花絮。我在团队聊天中问了我的同伴 Gophers,但似乎没人能找到相关的线索。

最后,我直接向 Russ Cox( @_rsc [1] )发送了一封电子邮件,以了解背景。

time.minWall

一个 const 值 time.minWall 设置为 1885,在如下代码中:

  • src/time/time.go [2]
 const (
    hasMonotonic = 1 << 63
    maxWall      = wallToInternal + (1<<33 - 1) // year 2157
    minWall      = wallToInternal               // year 1885
    nsecMask     = 1<<30 - 1
    nsecShift    = 30
)
  

这是个常数值,它定义了时间包中值的时代。这是一个常量值,它定义了 time 包的时间纪元。我们经常使用 Unix 纪元(即 1970 年 1 月 1 日 UTC 的 00:00:00),但这只是几个用于表示日期时间值的标准或实现中的一个纪元,当然 Go 是一种多平台语言,因此 Go 中的纪元需要涵盖所有这些平台。

Russ Cox 在以下地方公开评论了 Go 的纪元。第一个是在 GitHub issue 上 [3]

已合入 Go 1.9。我将内部纪元移到 1885 年(最大年份为 2157 年),以避免 NTP 纪元派生的时间出现任何可能的问题。我还调整了设计文档,以调整此更改和一些较小的编码更改。

但是这个评论只提到了为什么以及何时将内部纪元移到 1885 年,而没有提及为什么他们没有移至其他年份,例如 1900 年。

第二个是内部纪元移动的提案文档。

  • 提案:Go 中的单调时间测量 [4]

基于 Unix 的系统通常使用 1970,而基于 Windows 的系统通常使用 1980。我们不知道任何使用更早默认壁钟时间(Wall Time)的系统,但是由于 NTP 协议纪元使用 1900,因此选择 1900 之前的年份似乎更具前瞻性。

可见,理论依据也只是支持 1900 年之前的任何年份,而不是特指 1885 年。我几乎可以肯定,这一年来自“ 回到未来 3” ,但我想 100% 确定这一年,所以我联系了肯定知道这一点的人,即向 Russ Cox 发送了一封电子邮件,询问原因。他在一天之内做出了回应(考虑到 EDT 和 JST 之间的时区差异,这是非常快的):

是的,人们说服我移到 1900 年之前,而 1885 年是显而易见的选择,因为它对加利福尼亚的希尔山谷(Hill Valley)具有历史意义。:-)

这就是我知道的!!同样,我很高兴能够从谁做出决定中得到真正的答案。

http.aLongTimeAgo

尽管我很欣赏 Russ 的回答,但这还不是故事的结局。Russ 的回信中还有另外一行。

另请参见 http.aLongTimeAgo,现在将其设置为 time.Unix(1, 0),但以前是 time.Unix(233431200, 0)。

正如他所说,在 Go1.15 中,它设置为 time.Unix(1, 0)

  • Go 1.15: src/net/http/http.go [5]
 // aLongTimeAgo is a non-zero time, far in the past, used for
// immediate cancellation of network operations.
 var  aLongTimeAgo = time.Unix(1, 0)
  

因此,我们在源码中确认其原始值。你可以在 Go 1.8 中找到它。

  • Go 1.8: src/net/http/http.go [6]
 // aLongTimeAgo is a non-zero time, far in the past, used for
// immediate cancelation of network operations.
var aLongTimeAgo = time.Unix(233431200, 0)
  

我们将 UNIX 时间转换为人类可读的格式。当然,Go 提供了超级简单的方法。

  • 示例代码:以人类可读的格式读取 UNIX 时间 [7]
 package main

import (
    "fmt"
    "time"
)

func main() {
    pdt, _ := time.LoadLocation("America/Los_Angeles")
    t := time.Unix(233431200, 0).In(pdt)
    fmt.Println(t)
}
  

结果是:

 1977-05-25 11:00:00 -0700 PDT
  

我们得到 2 条提示:“A Long Time Ago”和 “1977-05-25”。这两个是:

当然,除了 《星球大战:第四 外,别无其他。它于1977年5月25日发布的Wikipedia:

Go 包中如何使用此值呢?这是通过指定过去时间的截止日期来强制取消现有连接,例如:

 cr.conn.rwc.SetReadDeadline(aLongTimeAgo)
  

当该值被改为 time.Unix(1, 0) 时,有人注意到了,因此对此作了一些评论(比如 1977-05-25 是谁的生日吗?)。我喜欢看到这些评论。这些有趣的聊天有时会在更改列表中进行。

  • net, net/http: 调整过去的时间到更早的时间 [8]

致谢

  • Russ Cox:感谢您回答我的问题和其他信息。另外,感谢您允许公开分享此内容。引用他的评论:

放心吧。这里没有秘密。

  • Chris Broadfoot [9] Valentin Deleplace [10] :感谢你们在群聊中一起找到线索。
  • mattn [11] :感谢您让我知道人们对此价值 有何 [12] 反应的 GitHub 评论线。

注意

该文最初于 2020-07-17 发布于我的日语博客中。

原文链接:

作者:Yoshi Yamaguchi

编译:polarisxu

参考资料

[1]

@_rsc:

[2]

src/time/time.go: #L153

[3]

GitHub issue 上: #issuecomment-277335863

[4]

提案:Go 中的单调时间测量:

[5]

Go 1.15: src/net/http/http.go: #L30

[6]

Go 1.8: src/net/http/http.go: #L23

[7]

示例代码:以人类可读的格式读取 UNIX 时间:

[8]

net, net/http: 调整过去的时间到更早的时间:

[9]

Chris Broadfoot:

[10]

Valentin Deleplace:

[11]

mattn:

[12]

有何:

相关文章