一瞥之见

0%

这次来搞懂计算机里面的时间

背景说明

用了很久的时间,但是最近在看《Linux/UNIX系统编程手册》-第10章(时间)以及通读了Go的time包的文档后发现对于常使用的时间可谓是知之甚少,不过目前自认感觉是认知比较清楚了,特此记录

名词解释

  • UTC/GMT:通用协调时间/格林威治标准时间(大概也是unix诞生的时间),即1970-01-01 00:00:00
  • DST:夏令时 - daylight time,即在高纬度地区,由于夏冬季节的白昼时间相差很大(夏季比冬季长),有些地区会在夏季将时间调快的做法,亚洲和非洲基本都不实施(包括中国)
  • CST:China Standard Time,UTC/gMT+0800(即表示在通用协调时间要+8小时)(+0800的意思来源于+hhmm)
  • Location:时区,核心构成有 名字(如CST),对于UTC时间的偏移(如+0800),是否使用夏令时,…

概念细化

程序所拥有的时间类型

1. 日历时间 - Wall clock:
  • Unix系统本身记录的值都是自UTC以来的秒数和纳秒数(*)
  • Go中的Time
    • 数据结构:
      1
      2
      3
      4
      5
      6
        type Time struct {
      wall uint64
      ext int64

      loc *Location // nil location means UTC
      }
    • 结构说明:
      • wall的组成(从高到低):第1位flag表示是否有monotonic clock;33位表示秒;30位表示nanoseconds;
      • 若无monotonic,则33位必须为0,然后完整的64为的ext存储wall seconds since 1年1月1日;
      • 若有monotonic,33位存储自1885年1月1日以来的wall seconds,ext的64位存储有符号的monotonic reading(表示的是nanoseconds since process start)
      • loc:指向所用时区信息的指针
    • 以fmt.Println(time.Now())为例看看其输出的Local的日历时间(先忽略单调时间部分)是什么样子:
      • 2020-10-10 15:19:04.094973 +0800 CST
      • 拆分来看就是:
        • 日期:2020-10-10
        • 时钟:15:19:04.094973
        • 所在时区对比UTC的偏移量:+0800
        • Local所在时区的名字:CST
      • 过程说明:(*)说了系统记录的是自UTC以来的时间戳,那么Go这里是如何输出到本地时间的呢?
        • /usr/share/zoneinfo目录下记录了各个时区对应于UTC时间的偏移以及时区的名字
        • /etc/localtime是一个link,其链接到/usr/share/zoneinfo目录下的某一个文件,表达的是此计算机的系统本地时间就是此时区
        • 结合以上两点就知道了,程序在启动时候通过/etc/localtime获取了Local的时区信息,故对从(*)获取的时间做相应的偏移即可
        • 备注:也有办法覆盖/etc/localtime为程序设定特定的时区:设置环境变量TZ(注意,一般此环境变量应该不存在,不存在才会去使用/etc/localtime,若存在且值为空,会使用UTC,否则使用指定的值)
  • 注意点:程序中的日历时间不一定是单调递增的,如果系统要和某个网络的时间同步、甚至是就是去修改了系统时间那么可能就会导致时间回到过去
  • 特别说明两个常用到的规范时间format的标准:
    • ISO 8601
      • e.g:2004-05-03T17:30:08+08:00(日期和时间合并表示时,要在时间前面加一大写字母T)
      • e.g:2020-10-19T00:15:38Z(如果时间在零时区,并恰好与协调世界时相同,那么(不加空格地)在时间最后加一个大写字母Z。Z是相对协调世界时时间0偏移的代号。)
    • RFC 3339:来源于ISO8601并为了简化ISO8601(只有一点点的不同),建议程序员阅读和使用此规范
      • 与 ISO 8601不同的地方:
        • ISO 8601 permits the hour to be “24”, this profile of ISO 8601 only allows values between “00” and “23” for the hour in order to reduce confusion.
      • 总结来说,推荐在格式化表示日期和时间时使用此格式2004-05-03T17:30:08+08:00
      • e.g: 在Go中指定使用RFC3339规范的例子(如此只要项目规定好都使用此规范,在解析时就不用去记录2006-01-02 15:04:05这种了,而且抽象度/灵活度更高)
        1
        2
        3
        4
        t, _ = time.Parse(time.RFC3339, "2006-01-02T15:04:05Z")
        fmt.Println(t) // 2006-01-02 15:04:05 +0000 UTC
        t, _ = time.Parse(time.RFC3339, "2006-01-02T15:04:05+07:00")
        fmt.Println(t) // 2006-01-02 15:04:05 +0700 +0700
2. 单调时间 - Monotonic clock:
  • 可以认为是此程序自开始以来占用的cpu时间(还是精确到纳秒的时间戳),故其一定是单调递增的(单调时间只在同进程内有比较等意义)
  • 以go语言为例来看看(以下为在go playground中运行的例子):
    • 代码
      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      package main
      import (
      "fmt"
      "time"
      )

      func main() {
      time.Sleep(time.Second * 2)
      fmt.Println(time.Now())
      }
    • 输出:2009-11-10 23:00:02 +0000 UTC m=+2.000000001
      • 最后的那个m=+2.000000001即是程序运行的Monotonic clock
    • 说明:Go中计算两个Time instance的差值时(t.After(u), t.Before(u), t.Equal(u), t.Sub(u))、如果都有单调时间,则优先使用单调时间
  • 注意,在Go中如果使用==来比较两个Time实例,在Monotonic clock存在的情况下也会纳入比较!

详细说明

由于我在使用marginnote 3记录笔记的,实际其中还记录了很多更为细节的东西,但是其导出PDF却总是不成功,只能通过导出到ithoughts,但是其中的细节又被变成来注释。权且当目录看吧(完全版可下载附件并用marginnote3打开)

笔记目录

  • 此颜色主要知识来自书Linux/UNIX系统编程手册
  • 此颜色表示是unix command
  • 此颜色表示是Go语言相关的内容(主要来自Go官方time包的文档)
    time_about_mindmapping

笔记详细(使用MarginNote 3打开)

附件地址