首页 > 解决方案 > 如何获取本地时区的全名?

问题描述

例如,服务器时区是“欧洲/马德里”,我这样做:

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

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

但我想要“欧洲/马德里”

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

编辑

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

所有功劳归于 colm.anseoMrFuppes提供以下答案:

标签: gotimezone

解决方案


IANA 时区可在大多数操作系统 (*) 上使用。Go标准库也提供它:

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

按名称选择时区以及该名称是否记录在任何地方都由操作系统决定:

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本地操作系统时间(timezone.go) - 但这可以用TZ环境变量覆盖。

因此,可以通过符号链接目标路径推断本地操作系统时区的名称,如下所示:

// 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

推荐阅读