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 国际许可协议进行许可。