I'd like to write a method that will populate a Go Language array with the common timezones that are accepted by the time.Format()
call, for use in an HTML template (Form select to allow them to read and choose their timezone). Is there a common way to do this?
Get a list of valid time zones in Go
To get a list of time zones, you can use something like:
package main
import (
"fmt"
"os"
"strings"
)
var zoneDirs = []string{
// Update path according to your OS
"/usr/share/zoneinfo/",
"/usr/share/lib/zoneinfo/",
"/usr/lib/locale/TZ/",
}
var zoneDir string
func main() {
for _, zoneDir = range zoneDirs {
ReadFile("")
}
}
func ReadFile(path string) {
files, _ := os.ReadDir(zoneDir + path)
for _, f := range files {
if f.Name() != strings.ToUpper(f.Name()[:1]) + f.Name()[1:] {
continue
}
if f.IsDir() {
ReadFile(path + "/" + f.Name())
} else {
fmt.Println((path + "/" + f.Name())[1:])
}
}
}
output:
Africa/Abidjan
Africa/Accra
Africa/Addis_Ababa
Africa/Algiers
Africa/Asmara
Africa/Asmera
Africa/Bamako
Africa/Bangui
...
Fantastic! Though, I think there is a typo in
ReadFile("")
-- Shouldn't it be ReadFile(zoneDir)
? –
Lacewing Nope, no typo) It is because of recursion. Also it is more like prototype, so you can refine and integrate somewhere, but you probably need to store results instead of just printing. –
Seventh
Any idea how to include the default fallback data located inside of
$GOROOT/lib/time
? –
Centrist In MacOS 11, there is a file
+VERSION
in /usr/share/zoneinfo/
. You have to skip this file. –
Pillar There are some files in directories which are not TZ files on macOS and on some Linux distros. The best way to test it is to try load the zone with
time.LoadLocation()
and check for errors. If no error, the file is TZ file. if _, err := time.LoadLocation(tzFilename); err != nil { continue }
–
Flori @Centrist I’m not sure if such a solution deserves a full-fledged answer, so would rather use comments section. Here you can have a look at how I extracted the strings from
$GOROOT/lib/time
: github.com/esimonov/locache/blob/main/cache_test.go. Heavily influenced by the internals of time
package. –
Protease Here is an example: https://play.golang.org/p/KFGQiW5A1P-
package main
import (
"fmt"
"io/ioutil"
"strings"
"unicode"
)
func main() {
fmt.Println(GetOsTimeZones())
}
func GetOsTimeZones() []string {
var zones []string
var zoneDirs = []string{
// Update path according to your OS
"/usr/share/zoneinfo/",
"/usr/share/lib/zoneinfo/",
"/usr/lib/locale/TZ/",
}
for _, zd := range zoneDirs {
zones = walkTzDir(zd, zones)
for idx, zone := range zones {
zones[idx] = strings.ReplaceAll(zone, zd+"/", "")
}
}
return zones
}
func walkTzDir(path string, zones []string) []string {
fileInfos, err := ioutil.ReadDir(path)
if err != nil {
return zones
}
isAlpha := func(s string) bool {
for _, r := range s {
if !unicode.IsLetter(r) {
return false
}
}
return true
}
for _, info := range fileInfos {
if info.Name() != strings.ToUpper(info.Name()[:1])+info.Name()[1:] {
continue
}
if !isAlpha(info.Name()[:1]) {
continue
}
newPath := path + "/" + info.Name()
if info.IsDir() {
zones = walkTzDir(newPath, zones)
} else {
zones = append(zones, newPath)
}
}
return zones
}
Go's time pkg uses a timezone database.
You can load a timezone location like this:
loc, err := time.LoadLocation("America/Chicago")
if err != nil {
// handle error
}
t := time.Now().In(loc)
The Format
function is not related to setting the time zone, this function takes a fixed reference time that allows you to format the date how you would like. Take a look at the time pkg docs.
For instance:
fmt.Println(t.Format("MST")) // outputs CST
Here is a running example
It doesn't answer the question. Why is it so much upvoted. –
Betts
The questions is a bit unclear. The question mistakes how the
Format
method works. This answer explains how to load the correct location and display the time using the loaded location. Displaying a list in an html template seems to be the easy part, so I assume people find what wrote useful, hence the upvotes. –
Scheldt Refined the answer provided by @Marsel,
Checks for Valid Time Zones, Automates OS lookup, and has no recursion (for readability) "Matter of preference".
Checked on "Ubuntu Server" and "Mint"
Hope it helps
package main
import (
"fmt"
"log"
"os"
"runtime"
"strings"
"time"
)
var zoneDirs = map[string]string{
"android": "/system/usr/share/zoneinfo/",
"darwin": "/usr/share/zoneinfo/",
"dragonfly": "/usr/share/zoneinfo/",
"freebsd": "/usr/share/zoneinfo/",
"linux": "/usr/share/zoneinfo/",
"netbsd": "/usr/share/zoneinfo/",
"openbsd": "/usr/share/zoneinfo/",
"solaris": "/usr/share/lib/zoneinfo/",
}
var timeZones []string
func main() {
// Reads the Directory corresponding to the OS
dirFile, _ := os.ReadDir(zoneDirs[runtime.GOOS])
for _, i := range dirFile {
// Checks if starts with Capital Letter
if i.Name() == (strings.ToUpper(i.Name()[:1]) + i.Name()[1:]) {
if i.IsDir() {
// Recursive read if directory
subFiles, err := os.ReadDir(zoneDirs[runtime.GOOS] + i.Name())
if err != nil {
log.Fatal(err)
}
for _, s := range subFiles {
// Appends the path to timeZones var
timeZones = append(timeZones, i.Name()+"/"+s.Name())
}
}
// Appends the path to timeZones var
timeZones = append(timeZones, i.Name())
}
}
// Loop over timezones and Check Validity, Delete entry if invalid.
// Range function doesnt work with changing length.
for i := 0; i < len(timeZones); i++ {
_, err := time.LoadLocation(timeZones[i])
if err != nil {
// newSlice = timeZones[:n] timeZones[n+1:]
timeZones = append(timeZones[:i], timeZones[i+1:]...)
continue
}
}
// Now we Can range timeZones for printing
for _, i := range timeZones {
fmt.Println(i)
}
}
Produces Output:
Africa/Abidjan
Africa/Accra
Africa/Addis_Ababa
Africa/Algiers
Africa/Asmara
Africa/Asmera
Africa/Bamako
Africa/Bangui
Africa/Banjul
Africa/Bissau
Africa/Blantyre
Africa/Brazzaville
...
Playground Link: https://go.dev/play/p/DoisnTppfnp
© 2022 - 2025 — McMap. All rights reserved.
func (t Time) Format(layout string)
doesn't take a time zone (other than "MST" to indicate where you want the time zone to go in the output). – Nessa