Go 标准库 time 包之 Location 类型 ( 上 )

时区 ( Location ) 想必大家不陌生,其实啊,它有另一个更通用的英文 time zone,不过呢,编程语言中爱用 loctzinfo 作为变量名。

我们先来了解下时区方面的知识。

大家都知道,地球分为 24 时区,有东 12 区和西 12 区,每个区跨越 7.5 度。

每个时区的中央经线上的时间就是这个时区内统一采用的时间,称为区时。相邻两个时区的时间相差1小时

例如,我国东8区的时间总比泰国东7区的时间早1小时,而比日本东9区的时间晚1小时。因此,出国旅行的人,必须随时调整自己的手表,才能和当地时间相一致。凡向西走,每过一个时区,就要把表向前拨1小时(比如2点拨到1点);凡向东走,每过一个时区,就要把表向后拨1小时(比如1点拨到2点)

格林尼治标准时间 ( GMT )

0 区是本初子午线经过的时区,其实又称为 格林尼治标准时间 ( GMT ) ,指位于英国伦敦郊区的皇家格林尼治天文台当地的标准时间,因为本初子午线被定义为通过那里的经线。

原因在于地球每天的自转是有些不规则的,而且正在缓慢减速,因此格林尼治时间基于天文观测本身的缺陷,已经不再被作为标准时间使用。

现在的标准时间,是由原子钟报时的协调世界时 ( UTC ) 来决定

UTC

协调世界时,英语:Coordinated Universal Time,法语:Temps Universel Coordonné,简称 UTC ,是最主要的世界时间标准

如果时间是以协调世界时 ( UTC ) 表示,则在时间后面直接加上一个 Z ( 不加空格 )

Z 是协调世界时中 0 时区的标志。因此,09:30 UTC 就写作 09:30Z 或是 0930Z14:45:15 UTC 则为 14:45:15Z144515Z

UTC 时间也被叫做祖鲁时间,因为在北约音标字母中用 Zulu 表示 Z

UTC 偏移量

UTC 偏移量是协调世界时 ( UTC ) 和特定时区的日期与时间差异,其单位为小时和分钟

UTC 偏移量通常以 ±[hh]:[mm]±[hh][mm]±[hh] 的格式显示

所以,如果被描述的时间比 UTC 早一小时 ( 例如柏林的冬季时间 ),UTC 的偏移量将是 +01:00+0100 或简单显示为 +01

北京时间

北京时间,又名中国标准时间,是中国大陆的标准时间,比协调世界时快八小时 ( 即 UTC+8 ) ,与香港、澳门、台北、吉隆坡、新加坡等地的标准时间相同

Location 类型

了解了时区和 UTC 、UTC 偏移量相关的知识后,我们就可以很轻松的理解 Location 这个类型了

Location 时区,是将一个时刻 ( Time t ) 映射到当时正在使用的区域 ( 时区 ) 。通常,Location 表示在地理区域中使用的时间偏移的集合,例如中欧的 CESTCET

经过前面的学习,大家应该都知道,Time 对象表示的是一个时刻,是某个时间点以来经过的 wall time,这个和所在地域是无关的。但为了表示所在地域的更好理解的时间,已经有了这个说法

我们先来看看 Location 类型的定义

type Location struct {
    // contains filtered or unexported fields
}

看起来没有什么公开的属性,我们看看源码有那些私有属性

type Location struct {
    name string
    zone []zone
    tx   []zoneTrans

    // Most lookups will be for the current time.
    // To avoid the binary search through tx, keep a
    // static one-element cache that gives the correct
    // zone for the time when the Location was created.
    // if cacheStart <= t < cacheEnd,
    // lookup can return cacheZone.
    // The units for cacheStart and cacheEnd are seconds
    // since January 1, 1970 UTC, to match the argument
    // to lookup.
    cacheStart int64
    cacheEnd   int64
    cacheZone  *zone
}

私有属性真的很多,我们列一个表解释吧

属性 说明
name 时区的名称
zone 时区
tx 时区转换方式
cacheStart 时区偏移量的缓存开始
cacheEnd 时区偏移量的结束
cacheZone 缓存的时区

和时区有关的变量

同时,time 包中又定义了几个和 Location 相关的变量

  1. var utcLoc = Location{name: "UTC"}

    这是一个仅限于 time 包内部使用的变量,用于表示通用协调时间 ( UTC ),也就是 0 时区,偏移量为 0

  2. var UTC *Location = &utcLoc

    变量 UTC 表示通用协调时间 ( UTC ),注意,这个变量的首字母是大写的,也就是说可以在 time 包外使用

  3. var localLoc Location

    变量 locaLoc 表示的是一个本地时间,如果时区对象是 localLoc ,那么只会初始化一次

    翻看源码,我们可以发现以下代码

    var localLoc Location
    var localOnce sync.Once
    
    func (l *Location) get() *Location {
        if l == nil {
            return &utcLoc
        }
        if l == &localLoc {
            localOnce.Do(initLocal)
        }
        return l
    }
    

    如果你仔细翻看源码,就会发现,机会所有的 Location 类型的方法都会先调用 get() 方法来初始化时区

  4. var Local *Location = &localLoc

    定义一个本地的时区,因为首字母大写,所以可以在 time 包外使用

    如果你看过源码,就会知道,其实这个 Local 在刚导入包的时候是未知的,我们写一段代码来看看

    package main
    
    import (
        "fmt"
        "time"
    )
    
    const UINT32_MAX uint32 = ^uint32(0)
    
    func main() {
        fmt.Printf("time.Local 的初始值为:%#v\n", time.Local)
    }
    

    运行结果如下

    time.Local 的初始值为:&time.Location{name:"", zone:[]time.zone(nil), tx:[]time.zoneTrans(nil), cacheStart:0,         cacheEnd:0, cacheZone:(*time.zone)(nil
    

    但,一旦我们生成了一个 Time 对象并输出其字符串表现形式的时候,就立刻初始化了

    package main
    
    import (
        "fmt"
        "time"
    )
    
    const UINT32_MAX uint32 = ^uint32(0)
    
    func main() {
        fmt.Printf("time.Local 的初始值为:%#v\n", time.Local)
    
        t := time.Now()
        fmt.Println("当前时间是:", t)
    
        fmt.Printf("time.Local 的值为:%#v\n", time.Local)
    }
    

    运行结果为

    time.Local 的初始值为:&time.Location{name:"", zone:[]time.zone(nil), tx:[]time.zoneTrans(nil), cacheStart:0, cacheEnd:0, cacheZone:(*time.zone)(nil)}
    当前时间是: 2018-08-28 22:16:45.72168 +0800 CST m=+0.000963491
    time.Local 的值为:&time.Location{name:"Local", zone:[]time.zone{time.zone{name:"CDT", offset:32400, isDST:true}, time.zone{name:"CST", offset:28800, isDST:false}}, tx:[]time.zoneTrans{time.zoneTrans{when:-933494400, index:0x0, isstd:false, isutc:false}, time.zoneTrans{when:-923130000, index:0x1, isstd:false, isutc:false}, time.zoneTrans{when:-908784000, index:0x0, isstd:false, isutc:false}, time.zoneTrans{when:-891594000, index:0x1, isstd:false, isutc:false}, time.zoneTrans{when:515520000, index:0x0, isstd:false, isutc:false}, time.zoneTrans{when:527007600, index:0x1, isstd:false, isutc:false}, time.zoneTrans{when:545155200, index:0x0, isstd:false, isutc:false}, time.zoneTrans{when:558457200, index:0x1, isstd:false, isutc:false}, time.zoneTrans{when:576604800, index:0x0, isstd:false, isutc:false}, time.zoneTrans{when:589906800, index:0x1, isstd:false, isutc:false}, time.zoneTrans{when:608659200, index:0x0, isstd:false, isutc:false}, time.zoneTrans{when:621961200, index:0x1, isstd:false, isutc:false}, time.zoneTrans{when:640108800, index:0x0, isstd:false, isutc:false}, time.zoneTrans{when:653410800, index:0x1, isstd:false, isutc:false}, time.zoneTrans{when:671558400, index:0x0, isstd:false, isutc:false}, time.zoneTrans{when:684860400, index:0x1, isstd:false, isutc:false}}, cacheStart:684860400, cacheEnd:9223372036854775807, cacheZone:(*time.zone)(0xc0000680e0)}
    

结束语

篇幅有限,剩下的方法我们下一章节再来讲述,下一章节,我们还会顺带讲讲 zone[] 那一坨代码到底是啥回事

关于   |   FAQ   |   我们的愿景   |   广告投放   |  博客

  简单教程,简单编程 - IT 入门首选站

Copyright © 2013-2022 简单教程 twle.cn All Rights Reserved.