3

Golang time 库里的一个矛盾实现

 2 years ago
source link: https://diabloneo.github.io//2021/10/20/Contradictory-implementation-in-Golang-time-library/
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.
neoserver,ios ssh client

Golang time 库里的一个矛盾实现

Oct 20, 2021

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
}

About Joyk


Aggregate valuable and interesting links.
Joyk means Joy of geeK