为什么 Go 的 time.Parse() 不解析时区标识符?

Svi*_*vip 7 go

考虑以下代码:

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
}
Run Code Online (Sandbox Code Playgroud)

您也可以在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
Run Code Online (Sandbox Code Playgroud)

我原以为第一行和第三行是一样的,第二行和第四行是一样的。

在我看来,Go 的时间库没有解析我在日期字符串中写入的“EDT”时区标识符,尽管它是格式的一部分。

我自己的系统 (Fedora 26) 也将 EST/EDT 识别为时区:

$ TZ='America/New_York' date 080112002018
Wed  1 Aug 12:00:00 EDT 2018
Run Code Online (Sandbox Code Playgroud)

当然,正如您所看到的,我可以通过使用来强制解决问题ParseInLocation(),但这只有在我事先知道时区时才有用。否则,我需要自己将日期字符串的“EDT”部分解析为“America/New_York”。

或者我错过了什么?

Ole*_*yar 2

一个简单的调试运行表明这一切都归结为这个函数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
}
Run Code Online (Sandbox Code Playgroud)

在我的 OSX 上(我CET现在在苏黎世) ,l.get()对区域切片中包含 4 个值的对象的调用返回CET,分别是CESTCETCEST。最重要的是GMT,并UTC事先经过特殊处理。所有其他区域对我来说都是“未知”的,包括 EDT。