[英]Why doesn't Go's time.Parse() parse the timezone identifier?
考虑以下代码:
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
}
您也可以在Go Playground上试用。
当我运行它时,我得到了这个结果(在我自己的系统上和通过 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
我原以为第一行和第三行是一样的,第二行和第四行也是一样的。
在我看来,Go 的时间库不会解析我在日期字符串中写入的“EDT”时区标识符,尽管它是格式的一部分。
我自己的系统(Fedora 26)也将 EST/EDT 识别为时区:
$ TZ='America/New_York' date 080112002018
Wed 1 Aug 12:00:00 EDT 2018
当然,如您所见,我可以使用ParseInLocation()
强制解决这个问题,但这只有在我事先知道时区时才有用。 否则我需要自己将日期字符串的“EDT”部分解析为“America/New_York”。
或者我错过了什么?
引用时间#Parse :
当解析带有像
MST
这样的时区缩写的时间时,如果时区缩写在当前位置有定义的偏移量,则使用该偏移量。
这里的关键是“当前位置”。 对我来说,这就是CST
。 使用它按预期工作:
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")
}
如果时区缩写未知,Parse 会将时间记录为处于具有给定时区缩写和零偏移量的虚构位置。
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")
}
为避免此类问题,请首选使用数字区域偏移量的时间布局,或使用 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")
}
}
一个简单的调试运行表明,这一切都归结为这个函数go/1.10/libexec/src/time/zoneinfo.go:226
func (l *Location) lookupName(name string, unix int64) (offset int, ok bool) {
l = l.get()
// First try for a zone with the right name that was actually
// in effect at the given time. (In Sydney, Australia, both standard
// and daylight-savings time are abbreviated "EST". Using the
// offset helps us pick the right one for the given time.
// It's not perfect: during the backward transition we might pick
// either one.)
for i := range l.zone {
zone := &l.zone[i]
if zone.name == name {
nam, offset, _, _, _ := l.lookup(unix - int64(zone.offset))
if nam == zone.name {
return offset, true
}
}
}
// Otherwise fall back to an ordinary name match.
for i := range l.zone {
zone := &l.zone[i]
if zone.name == name {
return zone.offset, true
}
}
// Otherwise, give up.
return
}
在我的 OSX 上(我在苏黎世,所以现在是CET
),对l.get()
的调用返回在区域切片中包含 4 个值的对象上,再次是CET
、 CEST
、 CET
和CEST
。 最重要的是, GMT
和UTC
事先经过特殊处理。 所有其他区域对我来说都是“未知”的,包括 EDT。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.