Why do 2 time structs with the same date and time return false when compared with ==?

huangapple go评论85阅读模式
英文:

Why do 2 time structs with the same date and time return false when compared with ==?

问题

我有一个使用time.Date()创建的time.Time对象。然后我计算了从1970/1/1 00:00:00.000000000到该时间之间的纳秒数。

然后,我将纳秒数转换回time.Time对象,使用的是time.Unix()函数。

然而,如果我使用==运算符比较重新生成的时间和原始时间,结果会返回false。如果我计算这两个时间的差值,得到的持续时间是0。如果我使用time.Equal()函数比较这两个时间,结果会返回true。

如果我使用与第一个时间相同的值,使用time.Date()创建另一个时间对象,使用==运算符比较这两个时间,结果会返回true。

这是一个演示这个问题的代码(Golang Playground):

package main

import (
	"fmt"
	"time"
)

func main() {
	t1 := time.Date(2016, 4, 14, 1, 30, 30, 222000000, time.UTC)
	
	
	base := time.Date(1970, 1, 1, 0, 0, 0, 0, t1.Location())
	nsFrom1970 :=t1.Sub(base).Nanoseconds() // 计算从1970/1/1到t1的纳秒数
	
	t2 := time.Unix(0, nsFrom1970)
	
	fmt.Println(t1)
	fmt.Println(t2)
	fmt.Println(t1.Sub(t2)) // 0
	fmt.Println(t1 == t2) //false
	fmt.Println(t1.Equal(t2)) //true
	
	t3 := time.Date(2100, 2, 1, 21, 21, 21, 222000000, time.UTC)
	fmt.Println(t1 == t3) //true
}

为什么重新生成的时间与原始时间比较时返回false呢?

英文:

I have a time.Time created using time.Date(). I then calculate the number of nanoseconds between 1970/1/1 00:00:00.000000000 and that time.

I then take the nanoseconds and turn them back into a time.Time using time.Unix().

However, if I compare the reconstituted time with the original using ==, it returns false. If I subtract these 2 times, the resulting duration is 0. If I compare these 2 times using time.Equal(), it returns true.

If I create another time using time.Date() with the same values as the first time, using == to compare this new time and the original time results in a true.

This is the code that demonstrates this (Golang Playground):

package main

import (
	"fmt"
	"time"
)

func main() {
	t1 := time.Date(2016, 4, 14, 1, 30, 30, 222000000, time.UTC)
	
	
	base := time.Date(1970, 1, 1, 0, 0, 0, 0, t1.Location())
	nsFrom1970 :=t1.Sub(base).Nanoseconds() // Calculate the number of nanoseconds from 1970/1/1 to t1
	
	t2 := time.Unix(0, nsFrom1970)
	
	fmt.Println(t1)
	fmt.Println(t2)
	fmt.Println(t1.Sub(t2)) // 0
	fmt.Println(t1 == t2) //false
	fmt.Println(t1.Equal(t2)) //true
	
	t3 := time.Date(2100, 2, 1, 21, 21, 21, 222000000, time.UTC)
	fmt.Println(t1 == t3) //true
}

Why does the reconstituted time return false when compared with the original time?

答案1

得分: 8

time.Time 是一个 struct。当你尝试用 == 进行比较时,引用自 规范:比较运算符

> 如果所有字段都是可比较的,那么结构体值是可比较的。如果两个结构体值的对应非空字段相等,则它们相等。

因此,t1 == t2 将比较两个 Time 结构体值的所有字段。Time 结构体不仅包含自基准时间以来的秒数和纳秒数,还包含位置作为指针:*Location,所以 == 也会比较位置字段。比较指针:

> 指针值是可比较的。如果两个指针值指向同一个变量或者都具有值 nil,则它们相等。指向不同的零大小变量的指针可能相等,也可能不相等。

这就是为什么用 == 比较时间会得到 false 的结果:即使地址不同,两个位置可能表示相同的位置,这就是你的情况。

为了证明这一点:

fmt.Println("Locations:", t1.Location(), t2.Location())
fmt.Printf("Location pointers: %p %p\n", t1.Location(), t2.Location())
fmt.Println("Locations equal:", t1.Location() == t2.Location())

输出:

Locations: UTC UTC
Location pointers: 0x1e2100 0x1e6de0
Locations equal: false

这在 time.Time 中有记录:

> 请注意,Go 的 == 运算符不仅比较时间瞬间,还比较位置。因此,Time 值在用作映射或数据库键之前,应确保为所有值设置了相同的位置,可以通过使用 UTC 或 Local 方法来实现。

如果 t1t2 还包含相同的 *Location 指针,即使使用 == 运算符进行比较,它们也会相等。可以通过在它们上调用 Time.UTC()Time.Local() 方法来确保这一点,这些方法返回一个使用相同位置指针(*Location)的 time.Time 值。或者通过使用 Time.In() 方法来 设置 指定的位置指针(在适当的转换之后),例如:

t2 = t2.In(t1.Location())
fmt.Println("Locations:", t1.Location(), t2.Location())
fmt.Printf("Location pointers: %p %p\n", t1.Location(), t2.Location())
fmt.Println("Locations equal:", t1.Location() == t2.Location())
fmt.Println(t1 == t2)     // 现在为 true
fmt.Println(t1.Equal(t2)) // 仍然为 true

输出:

Locations: UTC UTC
Location pointers: 0x1e2100 0x1e2100
Locations equal: true
true
true

Go Playground 上试一试。

英文:

time.Time is a struct. When you try to compare them with ==, quoting from the Spec: Comparison operator:

> Struct values are comparable if all their fields are comparable. Two struct values are equal if their corresponding non-blank fields are equal.

So t1 == t2 will compare all the fields of the 2 Time struct values. The Time struct contains not just the second and nanosecond since a base time, it also contains the location as a pointer: *Location, so == will also compare the location fields. Comparing pointers:

> Pointer values are comparable. Two pointer values are equal if they point to the same variable or if both have value nil. Pointers to distinct zero-size variables may or may not be equal.

And this is why comparing the times with == gives a false result: 2 locations may denote the same location even if their address is different, and this is your case.

To prove this:

fmt.Println("Locations:", t1.Location(), t2.Location())
fmt.Printf("Location pointers: %p %p\n", t1.Location(), t2.Location())
fmt.Println("Locations equal:", t1.Location() == t2.Location())

Output:

Locations: UTC UTC
Location pointers: 0x1e2100 0x1e6de0
Locations equal: false

This is documented in time.Time:

> Note that the Go == operator compares not just the time instant but also the Location. Therefore, Time values should not be used as map or database keys without first guaranteeing that the identical Location has been set for all values, which can be achieved through use of the UTC or Local method.

If t1 and t2 would also contain the same *Location pointer, they would be equal even if compared with the == operator. This can be ensured by calling Time.UTC() or Time.Local() method on them which returns a time.Time value where the same location pointer (*Location) is used. Or by using the Time.In() method which will set the specified location pointer (after the proper conversion), e.g.:

t2 = t2.In(t1.Location())
fmt.Println("Locations:", t1.Location(), t2.Location())
fmt.Printf("Location pointers: %p %p\n", t1.Location(), t2.Location())
fmt.Println("Locations equal:", t1.Location() == t2.Location())
fmt.Println(t1 == t2)     // Now true
fmt.Println(t1.Equal(t2)) // Still true

Output:

Locations: UTC UTC
Location pointers: 0x1e2100 0x1e2100
Locations equal: true
true
true

Try it on the Go Playground.

huangapple
  • 本文由 发表于 2016年4月14日 14:03:06
  • 转载请务必保留本文链接:https://go.coder-hub.com/36614921.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定