Golang time 库里的一个矛盾实现
Golang time.Now()
和 time.Parse()
两个函数对于 location 处理上是不一致的。
time.Parse()
方法在生成 Time
对象时,可能会调用 setLoc
方法,用于设置时区信息。setLoc
方法,以及相关的其他方法都有一个特殊处理,就是将 Time.loc == nil
这个条件等同于 UTC 时区,例如下列代码:
// setLoc sets the location associated with the time.
func (t *Time) setLoc(loc *Location) {
if loc == &utcLoc {
loc = nil
}
t.stripMono()
t.loc = loc
}
但是,time.Now()
方法并不是这么处理的,无论时区如何,它都会为 Time.loc
赋值,如下所示:
// Now returns the current local time.
func Now() Time {
sec, nsec, mono := now()
mono -= startNano
sec += unixToInternal - minWall
if uint64(sec)>>33 != 0 {
return Time{uint64(nsec), sec + minWall, Local}
}
return Time{hasMonotonic | uint64(sec)<<nsecShift | uint64(nsec), mono, Local}
}
于是,当你的程序运行在一个 UTC 时区的 Linux 系统上时,就会出现如下矛盾的现象:time.Now()
返回的值里, loc
成员是有值的,但是用 time.Parse()
去解析一个时间字符串时,得到的值里,loc
成员可能是 nil
。这个矛盾会导致一些比较代码失败(直接用 ==
比较),因为其中的 loc
字段的值不一致。当然,官方文档也提到了这一点,所以比较时间还是应该用 Time.Equal()
方法。
Note that the Go == operator compares not just the time instant but also the Location and the monotonic clock reading.
如果需要在这种情况下,为 Parse()
得到的时间附上一个时区信息,得到和 Now()
一样的效果,可以使用如下的函数实现:
// parseTime calls time.Parse and set location if time.Local is UTC.
//
// time.Parse function will call Time.setLoc method which set Time.loc = nil
// if location is UTC. But on a machine with UTC timezone, time.Now() returns
// a Time value with Time.loc set to utcLoc, so it's not nil. The two methods
// result in discrepancy in time.Time values.
func parseTime(layout, value string) (time.Time, error) {
t, err := time.Parse(layout, value)
if err != nil {
return time.Time{}, err
}
if _, offset := t.Zone(); offset == 0 {
return t.Local(), nil
}
return t, nil
}
本作品采用知识共享署名-非商业性使用-禁止演绎 4.0 国际许可协议进行许可。