如何获取当地时区的全名?

How to get the full name of the local timezone?

例如服务器时区是“Europe/Madrid”,我这样做:

now := time.Now()
fmt.Printf("%v", now.Location().String()) // prints "Local"

zone, _ := now.Zone()
fmt.Printf("%v", zone) // prints "CEST"

但我想要“Europe/Madrid”

时区:https://en.wikipedia.org/wiki/List_of_tz_database_time_zones

编辑

完全跨平台(windows、linux、mac)此处提供答案:https://github.com/thlib/go-timezone-local

所有功劳归功于 colm.anseo and MrFuppes 提供以下答案:

IANA timezones 在大多数 OSes (*) 上可用。 Go 标准库也提供了它:

runtime.GOROOT() + "/lib/time/zoneinfo.zip"

name选择时区以及这个名字是否记录在任何地方取决于OS:

ls -al /etc/localtime

# MacOS
/etc/localtime -> /var/db/timezone/zoneinfo/America/New_York

# Linux
/etc/localtime -> ../usr/share/zoneinfo/America/New_York

因此在上述情况下可以推断出名称。

注意:Go 默认使用 /etc/localtime 作为本地 OS 时间 (timezone.go) - 但可以用 TZ 环境覆盖变量。

因此可以通过符号链接目标路径推断出本地 OS 时区的名称:

// tz.go

//go:build !windows
// +build !windows

const localZoneFile = "/etc/localtime" // symlinked file - set by OS
var ValidateLocationName = true        // set to false to disable time.LoadLocation validation check

func LocalTZLocationName() (name string, err error) {

    var ok bool
    if name, ok = os.LookupEnv("TZ"); ok {
        if name == "" { // if blank - Go treats this as UTC
            return "UTC", nil
        }
        if ValidateLocationName {
            _, err = time.LoadLocation(name) // optional validation of name
        }
        return
    }

    fi, err := os.Lstat(localZoneFile)
    if err != nil {
        err = fmt.Errorf("failed to stat %q: %w", localZoneFile, err)
        return
    }

    if (fi.Mode() & os.ModeSymlink) == 0 {
        err = fmt.Errorf("%q is not a symlink - cannot infer name", localZoneFile)
        return
    }

    p, err := os.Readlink(localZoneFile)
    if err != nil {
        return
    }

    name, err = inferFromPath(p) // handles 1 & 2 part zone names
    return
}

func inferFromPath(p string) (name string, err error) {

    dir, lname := path.Split(p)

    if len(dir) == 0 || len(lname) == 0 {
        err = fmt.Errorf("cannot infer timezone name from path: %q", p)
        return
    }

    _, fname := path.Split(dir[:len(dir)-1])

    if fname == "zoneinfo" {
        name = lname // e.g. /usr/share/zoneinfo/Japan
    } else {
        name = fname + string(os.PathSeparator) + lname // e.g. /usr/share/zoneinfo/Asia/Tokyo
    }

    if ValidateLocationName {
        _, err = time.LoadLocation(name) // optional validation of name
    }

    return
}

// tz_windows.go
//go:build windows
// +build windows

func LocalTZLocationName() (string, error) {
    return "", fmt.Errorf("local timezone name inference not available on Windows")
}

(*) 对于 Windows 根据 Go 源 (zoneinfo_windows.go),区域信息是从 Go 标准库加载的,由于:

// BUG(brainman,rsc): On Windows, the operating system does not provide complete
// time zone information.
// The implementation assumes that this year's rules for daylight savings
// time apply to all previous and future years as well.

Window 的注册表项:

HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Control\TimeZoneInformation

存储本地时区 - 但似乎不存储长格式位置名称:

TimeZoneKeyName=Eastern Standard Time

在 Windows 上,该过程也并不过分复杂。作为 ,您可以读取相应的注册表项以获取 Windows 使用的时区名称。然后将其映射到适当的 IANA 时区名称。

从注册表读取 tz

package main

import (
    "log"

    "golang.org/x/sys/windows/registry"
)

var winTZtoIANA = map[string]string{
    // just includes my time zone; to be extended...
    "W. Europe Standard Time": "Europe/Berlin",
}

func main() {

    k, err := registry.OpenKey(registry.LOCAL_MACHINE, `SYSTEM\CurrentControlSet\Control\TimeZoneInformation`, registry.QUERY_VALUE)
    if err != nil {
        log.Fatal(err)
    }
    defer k.Close()

    winTZname, _, err := k.GetStringValue("TimeZoneKeyName")
    if err != nil {
        log.Fatal(err)
    }

    log.Printf("local time zone in Windows: %v\n", winTZname)

    if IANATZname, ok := winTZtoIANA[winTZname]; ok {
        log.Printf("local time zone IANA name: %v\n", IANATZname)
    } else {
        log.Fatalf("could not find IANA tz name for %v", winTZname)
    }

}

或者,您可以读取tzutil /g的输出:

cmd := exec.Command("tzutil", "/g")
winTZname, err := cmd.Output() // note: winTZname is []byte, need to convert to string
if err != nil {
    // handle error
}

您可以在 Python 中找到解决给定问题的综合 mapping here. It's based on Lennart Regebro's tzlocal 包。