Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Why doesn't Go's time.Parse() parse the timezone identifier?

Tags:

go

Consider the following code:

package main

import (
    "time"
    "fmt"
)

const (
    format = "2006 01 02 15:04 MST"

    date = "2018 08 01 12:00 EDT"
)

func main() {
    aloc, _ := time.LoadLocation("America/New_York")
    eloc, _ := time.LoadLocation("Europe/Berlin")
    tn, _ := time.Parse(format, date)
    tl, _ := time.ParseInLocation(format, date, aloc)

    fmt.Println(tn) // Says +0000 despite EDT being -0400
    fmt.Println(tn.In(eloc)) // Expect 18:00, but get 14:00
    fmt.Println(tl) // Correctly -0400
    fmt.Println(tl.In(eloc)) // Correctly 18:00
}

You can also try it out on Go Playground.

When I run it, I get this result (both on my own system and through the Playground):

2018-08-01 12:00:00 +0000 EDT
2018-08-01 14:00:00 +0200 CEST
2018-08-01 12:00:00 -0400 EDT
2018-08-01 18:00:00 +0200 CEST

I had expected the first and third line to be the same, and the second and fourth to be the same.

It seems to me that Go's time library doesn't parse the "EDT" timezone identifier I've written in the date string, despite it being part of the format.

My own system (Fedora 26) also recognises EST/EDT as a timezone:

$ TZ='America/New_York' date 080112002018
Wed  1 Aug 12:00:00 EDT 2018

Of course, as you can see, I can force the issue by using ParseInLocation(), but that's only useful if I know the timezone beforehand. Otherwise I need to parse the 'EDT' part of the date string into 'America/New_York' myself.

Or am I missing something?

like image 763
Svip Avatar asked Mar 03 '18 12:03

Svip


1 Answers

Quoting from time#Parse:

When parsing a time with a zone abbreviation like MST, if the zone abbreviation has a defined offset in the current location, then that offset is used.

The key here is "current location". For me, that is CST. Using this works as expected:

package main
import "time"

func main() {
   t, e := time.Parse(time.RFC1123, "Wed, 01 Aug 2018 12:00:00 CST")
   if e != nil {
      panic(e)
   }
   s := t.String()
   println(s == "2018-08-01 13:00:00 -0500 CDT")
}

If the zone abbreviation is unknown, Parse records the time as being in a fabricated location with the given zone abbreviation and a zero offset.

package main
import "time"

func main() {
   t, e := time.Parse(time.RFC1123, "Wed, 01 Aug 2018 12:00:00 EDT")
   if e != nil {
      panic(e)
   }
   s := t.String()
   println(s == "2018-08-01 12:00:00 +0000 EDT")
}

To avoid such problems, prefer time layouts that use a numeric zone offset, or use ParseInLocation.

package main
import "time"

func main() {
   { // example 1
      t, e := time.Parse(time.RFC1123Z, "Wed, 01 Aug 2018 12:00:00 -0400")
      if e != nil {
         panic(e)
      }
      s := t.String()
      println(s == "2018-08-01 12:00:00 -0400 -0400")
   }
   { // example 2
      ny, e := time.LoadLocation("America/New_York")
      if e != nil {
         panic(e)
      }
      t, e := time.ParseInLocation(time.RFC1123, "Wed, 01 Aug 2018 12:00:00 EDT", ny)
      if e != nil {
         panic(e)
      }
      s := t.String()
      println(s == "2018-08-01 12:00:00 -0400 EDT")
   }
}
like image 175
Zombo Avatar answered Sep 17 '22 01:09

Zombo