Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

breaking change in go 1.9 about time marshal/unmarshal #22251

Closed
yiminc-zz opened this issue Oct 13, 2017 · 8 comments
Closed

breaking change in go 1.9 about time marshal/unmarshal #22251

yiminc-zz opened this issue Oct 13, 2017 · 8 comments

Comments

@yiminc-zz
Copy link

Please answer these questions before submitting your issue. Thanks!

What version of Go are you using (go version)?

go version
go version go1.9.1 darwin/amd64

Does this issue reproduce with the latest release?

Yes

What operating system and processor architecture are you using (go env)?

go env
GOARCH="amd64"
GOBIN=""
GOEXE=""
GOHOSTARCH="amd64"
GOHOSTOS="darwin"
GOOS="darwin"
GOPATH="/Users/yiminc/gocode"
GORACE=""
GOROOT="/usr/local/Cellar/go/1.9.1/libexec"
GOTOOLDIR="/usr/local/Cellar/go/1.9.1/libexec/pkg/tool/darwin_amd64"
GCCGO="gccgo"
CC="clang"
GOGCCFLAGS="-fPIC -m64 -pthread -fno-caret-diagnostics -Qunused-arguments -fmessage-length=0 -fdebug-prefix-map=/var/folders/f6/2zwvng355csd30gym37w1yph0000gn/T/go-build213006030=/tmp/go-build -gno-record-gcc-switches -fno-common"
CXX="clang++"
CGO_ENABLED="1"
CGO_CFLAGS="-g -O2"
CGO_CPPFLAGS=""
CGO_CXXFLAGS="-g -O2"
CGO_FFLAGS="-g -O2"
CGO_LDFLAGS="-g -O2"
PKG_CONFIG="pkg-config"

What did you do?

Marshal and Unmarshal a time.Time instance, they become different.
The use case is we use testify to setup mock expectation with a time as expected parameter. Then the tested code internally would do a marshal/unmarshal, and pass the value to the mock call. But the mock call would fail because the expectation does not met. The testify uses reflect.DeepEqual to compare if the parameters are match or not.

However, if you marshal/unmarshal a zero time, then before == after.

https://play.golang.org/p/dX_knLlRY-

This used to work fine with go 1.8

What did you expect to see?

a time instance after marshal/unmarshal (on same machine) should not change.

What did you see instead?

a time instance after marshal/unmarshal (on same machine) are different.

@artyom
Copy link
Member

artyom commented Oct 13, 2017

Possible duplicate of #20876, please see its discussion.

@ianlancetaylor
Copy link
Contributor

This is expected behavior. You should not in general use reflect.DeepEqual to compare time.Time values. You should use time.Equal instead. The time package docs say:

// Note that the Go == operator compares not just the time instant but
// also the Location and the monotonic clock reading. See the
// documentation for the Time type for a discussion of equality
// testing for Time values.

This discussion of == applies to reflect.DeepEqual instead.

It is true that this became less likely to work in Go 1.9, due to the introduction of monotonic time values. But it was never fully safe even before Go 1.9, due to the way that timezones are handled.

Closing because this isn't going to change.

@yiminc-zz
Copy link
Author

Understand DeepEqual behavior. The question is more about why the time are changed after marshal/unmarshal.

@ianlancetaylor
Copy link
Contributor

I assume because of the monotonic time values. See the "Monotonic Clock" section at https://golang.org/pkg/time.

@yiminc-zz
Copy link
Author

Don't understand why do we need to change the monotonic clock over marshal/unmarshal process?

@ianlancetaylor
Copy link
Contributor

The monotonic time is meaningless in a different process, so we don't marshal it at all.

@calvn
Copy link

calvn commented Apr 2, 2018

Interestingly, time comparison with == or reflect.DeepEqual after using time.Truncate(0) seem to work on MacOS, but not in linux/Go Playground. I haven't figured out why yet, but thought it might be useful to report this behavior.

$ go env
GOARCH="amd64"
GOBIN=""
GOCACHE="/Users/cleung/Library/Caches/go-build"
GOEXE=""
GOHOSTARCH="amd64"
GOHOSTOS="darwin"
GOOS="darwin"
GOPATH="/Users/cleung/go"
GORACE=""
GOROOT="/usr/local/Cellar/go/1.10.1/libexec"
GOTMPDIR=""
GOTOOLDIR="/usr/local/Cellar/go/1.10.1/libexec/pkg/tool/darwin_amd64"
GCCGO="gccgo"
CC="clang"
CXX="clang++"
CGO_ENABLED="1"
CGO_CFLAGS="-g -O2"
CGO_CPPFLAGS=""
CGO_CXXFLAGS="-g -O2"
CGO_FFLAGS="-g -O2"
CGO_LDFLAGS="-g -O2"
PKG_CONFIG="pkg-config"
GOGCCFLAGS="-fPIC -m64 -pthread -fno-caret-diagnostics -Qunused-arguments -fmessage-length=0 -fdebug-prefix-map=/var/folders/1x/c_x5s2h90mzdyx4bzz3_dsx80000gn/T/go-build019210901=/tmp/go-build -gno-record-gcc-switches -fno-common"

Updated Go Playground using time.Truncate(0) https://play.golang.org/p/XWurzn_3NTZ:

********************
BEFORE: 0001-01-01 00:00:00 +0000 UTC
AFTER : 0001-01-01 00:00:00 +0000 UTC
BEFORE.Equals(AFTER) : true
BEFORE == AFTER : true
BEFORE DeepEqual AFTER : true
********************
BEFORE: 2009-11-10 23:00:00 +0000 UTC
AFTER : 2009-11-10 23:00:00 +0000 UTC
BEFORE.Equals(AFTER) : true
BEFORE == AFTER : false
BEFORE DeepEqual AFTER : false

Program exited.

MacOS locally:

$ go run main.go
********************
BEFORE: 0001-01-01 00:00:00 +0000 UTC
AFTER : 0001-01-01 00:00:00 +0000 UTC
BEFORE.Equals(AFTER) : true
BEFORE == AFTER : true
BEFORE DeepEqual AFTER : true
********************
BEFORE: 2018-04-02 12:56:10.065154507 -0400 EDT
AFTER : 2018-04-02 12:56:10.065154507 -0400 EDT
BEFORE.Equals(AFTER) : true
BEFORE == AFTER : true
BEFORE DeepEqual AFTER : true

@alexandrestein
Copy link

@calvn I have a similar behavior with Arch Linux. Go is installed via default package manage pacman.

go env
GOARCH="amd64"
GOBIN=""
GOCACHE="/home/alexandre/.cache/go-build"
GOEXE=""
GOFLAGS=""
GOHOSTARCH="amd64"
GOHOSTOS="linux"
GOOS="linux"
GOPATH="/home/alexandre/go"
GOPROXY=""
GORACE=""
GOROOT="/usr/lib/go"
GOTMPDIR=""
GOTOOLDIR="/usr/lib/go/pkg/tool/linux_amd64"
GCCGO="gccgo"
CC="gcc"
CXX="g++"
CGO_ENABLED="1"
GOMOD=""
CGO_CFLAGS="-g -O2"
CGO_CPPFLAGS=""
CGO_CXXFLAGS="-g -O2"
CGO_FFLAGS="-g -O2"
CGO_LDFLAGS="-g -O2"
PKG_CONFIG="pkg-config"
GOGCCFLAGS="-fPIC -m64 -pthread -fmessage-length=0 -fdebug-prefix-map=/tmp/go-build737376362=/tmp/go-build -gno-record-gcc-switches"

If I run the same code inside a Docker it behave as Playground.

@golang golang locked and limited conversation to collaborators Sep 6, 2019
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Projects
None yet
Development

No branches or pull requests

6 participants