(Edit/update): Thanks to @djd for pointing out that we can skip all the JSON/struct decoding business; the key issue is with time.Parse
.
The same issue comes up here where the Location
is "empty" rather than UTC (I would've expected UTC based on the docs: https://golang.org/pkg/time/#Parse
"In the absence of a time zone indicator, Parse returns a time in UTC."
Here's the example code: https://play.golang.org/p/pb3eMbjSmv
package main
import (
"fmt"
"time"
)
func main() {
// Ignoring the err just for this example's sake!
parsed, _ := time.Parse(time.RFC3339, "2017-08-15T22:30:00+00:00")
fmt.Printf("String(): %v
", parsed.String())
fmt.Printf("Location(): %v
", parsed.Location())
}
which outputs
String(): 2017-08-15 22:30:00 +0000 +0000
Location():
So while the offset of the time.Time
's Location
appears to be correct, its timezone name is just an empty string. Running in on other machines (and The Go Playground) give the expected "UTC" location.
[Original post]:
When decoding a timestamp field from JSON into a struct
on my local OS X machine, the Location
of the time.Time
field is "empty" rather than UTC. This is problematic for me running unit tests locally (vs. on a CI server where the Location
is being set correctly to be UTC).
When I run that on my machine, I see
go run main.go TimeField.String(): 2017-08-15 22:30:00 +0000 +0000 TimeField.Location():
So while the offset of the time.Time
's Location
appears to be correct, its timezone name is just an empty string. This is using Go 1.5:
go version go version go1.5 darwin/amd64
I find same behavior using my current setup on Mac and I suspect it will be same behavior on Linux (not sure through)
$ go version
go version devel +31ad583 Wed Aug 10 19:44:08 2016 +0000 darwin/amd64
To make it more deterministic, I suggest using a custom json Unmarshal like so:
package main
import (
"encoding/json"
"fmt"
"strings"
"time"
)
type Time struct {
*time.Time
}
func (t *Time) UnmarshalJSON(b []byte) error {
const format = "\"2006-01-02T15:04:05+00:00\""
t_, err := time.Parse(format, string(b))
if err != nil {
return err
}
*t = Time{&t_}
return nil
}
type Example struct {
TimeField *Time `json:"time_field"`
}
func main() {
inString := "{\"time_field\": \"2017-08-15T22:30:00+00:00\"}"
var ex Example
decoder := json.NewDecoder(strings.NewReader(inString))
decoder.Decode(&ex)
fmt.Printf("TimeField.String(): %v
", ex.TimeField.String())
fmt.Printf("TimeField.Location(): %v
", ex.TimeField.Location())
}
Yes, You are right. On The Go Playground the Local
is set to UTC
inside that sandbox:
Try this working sample code on The Go Playground:
package main
import (
"fmt"
"runtime"
"time"
)
func main() {
fmt.Println(runtime.Version(), runtime.GOARCH, runtime.GOOS) //go1.7 amd64p32 nacl
parsed, err := time.Parse(time.RFC3339, "2017-08-15T22:30:00+00:00")
if err != nil {
panic(err)
}
fmt.Printf("String(): %v
", parsed.String())
fmt.Printf("Location(): %v
", parsed.Location())
}
output on The Go Playground:
go1.7 amd64p32 nacl
String(): 2017-08-15 22:30:00 +0000 UTC
Location(): UTC
And try it on your local system, output Location()
is empty.
You may use utc := parsed.UTC()
with the location set to UTC, like this working sample code The Go Playground:
package main
import (
"fmt"
"runtime"
"time"
)
func main() {
fmt.Println(runtime.Version(), runtime.GOARCH, runtime.GOOS) //go1.7 amd64p32 nacl
parsed, err := time.Parse(time.RFC3339, "2017-08-15T22:30:00+00:00")
if err != nil {
panic(err)
}
fmt.Printf("String(): %v
", parsed.String())
fmt.Printf("Location(): %v
", parsed.Location())
utc := parsed.UTC()
fmt.Printf("String(): %v
", utc.String())
fmt.Printf("Location(): %v
", utc.Location())
}
Also You may use time.ParseInLocation(time.RFC3339, "2017-08-15T22:30:00+00:00", time.UTC)
, like this working sample code:
package main
import (
"fmt"
"runtime"
"time"
)
func main() {
fmt.Println(runtime.Version(), runtime.GOARCH, runtime.GOOS) //go1.7 amd64p32 nacl
parsed, err := time.ParseInLocation(time.RFC3339, "2017-08-15T22:30:00+00:00", time.UTC)
if err != nil {
panic(err)
}
fmt.Printf("String(): %v
", parsed.String())
fmt.Printf("Location(): %v
", parsed.Location())
}
So the Location()
will be UTC
.