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