英文:
How to get the full name of the local timezone?
问题
例如,服务器的时区是"Europe/Madrid",我执行以下操作:
now := time.Now()
fmt.Printf("%v", now.Location().String()) // 输出"Local"
zone, _ := now.Zone()
fmt.Printf("%v", zone) // 输出"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和MrFuppes,他们提供了以下答案:
英文:
For example the server timezone is "Europe/Madrid" and I do this:
now := time.Now()
fmt.Printf("%v", now.Location().String()) // prints "Local"
zone, _ := now.Zone()
fmt.Printf("%v", zone) // prints "CEST"
But I want "Europe/Madrid"
Timezones: https://en.wikipedia.org/wiki/List_of_tz_database_time_zones
Edit
Full cross platform (windows, linux, mac) answer provided here: https://github.com/thlib/go-timezone-local
All credit goes to colm.anseo and MrFuppes for providing the following answers:
答案1
得分: 2
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" // 符号链接文件 - 由操作系统设置
var ValidateLocationName = true // 设置为false以禁用time.LoadLocation验证检查
func LocalTZLocationName() (name string, err error) {
var ok bool
if name, ok = os.LookupEnv("TZ"); ok {
if name == "" { // 如果为空白 - Go将其视为UTC
return "UTC", nil
}
if ValidateLocationName {
_, err = time.LoadLocation(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不是符号链接 - 无法推断名称", localZoneFile)
return
}
p, err := os.Readlink(localZoneFile)
if err != nil {
return
}
name, err = inferFromPath(p) // 处理1和2部分的时区名称
return
}
func inferFromPath(p string) (name string, err error) {
dir, lname := path.Split(p)
if len(dir) == 0 || len(lname) == 0 {
err = fmt.Errorf("无法从路径推断时区名称:%q", p)
return
}
_, fname := path.Split(dir[:len(dir)-1])
if fname == "zoneinfo" {
name = lname // 例如:/usr/share/zoneinfo/Japan
} else {
name = fname + string(os.PathSeparator) + lname // 例如:/usr/share/zoneinfo/Asia/Tokyo
}
if ValidateLocationName {
_, err = time.LoadLocation(name) // 可选的名称验证
}
return
}
// tz_windows.go
//go:build windows
// +build windows
func LocalTZLocationName() (string, error) {
return "", fmt.Errorf("无法在Windows上推断本地时区名称")
}
(*)根据Go源代码(zoneinfo_windows.go),对于Windows
,时区信息是从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.
Windows注册表键:
HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Control\TimeZoneInformation
存储了本地时区,但似乎没有存储完整的位置名称:
TimeZoneKeyName=Eastern Standard Time
英文:
IANA timezones are available on most OSes (*). The Go
standard library ships it as well:
runtime.GOROOT() + "/lib/time/zoneinfo.zip"
The selection of a timezone by name and whether this name is recorded anywhere is left up to the OS:
ls -al /etc/localtime
# MacOS
/etc/localtime -> /var/db/timezone/zoneinfo/America/New_York
# Linux
/etc/localtime -> ../usr/share/zoneinfo/America/New_York
so in the above cases the name may be inferred.
Note: Go
uses /etc/localtime
by default for the local OS time (timezone.go) - but this can be overridden with the TZ
environment variable.
So one could infer the name of the local OS timezone, via the symlink target path like so:
// 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")
}
(*) for Windows
according to the Go source (zoneinfo_windows.go), zone info is loaded from the Go standard library, due to:
// 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.
the Window's registry key:
HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Control\TimeZoneInformation
stores the local timezone - but does not appear to store the long form location name:
TimeZoneKeyName=Eastern Standard Time
答案2
得分: 1
在Windows上,该过程也不是过于复杂的。如在stackoverflow上提到的@colm.anseo,你可以读取相应的注册表键来获取Windows使用的时区名称,然后将其映射到适当的IANA时区名称。
从注册表中读取tz:
package main
import (
"log"
"golang.org/x/sys/windows/registry"
)
var winTZtoIANA = map[string]string{
// 只包括我的时区;可以扩展...
"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("Windows本地时区:%v\n", winTZname)
if IANATZname, ok := winTZtoIANA[winTZname]; ok {
log.Printf("IANA时区名称:%v\n", IANATZname)
} else {
log.Fatalf("无法找到%v的IANA时区名称", winTZname)
}
}
或者,你可以读取tzutil /g
的输出:
cmd := exec.Command("tzutil", "/g")
winTZname, err := cmd.Output() // 注意:winTZname是[]byte类型,需要转换为字符串
if err != nil {
// 处理错误
}
你可以在这里找到一个全面的映射。它基于Lennart Regebro的tzlocal包,该包解决了Python中的给定问题。
英文:
On Windows, the procedure also isn't overly complex. As mentioned by @colm.anseo, you can read the respective registry key to get the time zone name that Windows uses. Then map that to the appropriate IANA time zone name.
read tz from registry
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)
}
}
Alternatively, you can read the output of 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
}
You can find a comprehensive mapping here. It's based on Lennart Regebro's tzlocal package that solves the given question in Python.
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论