Reputation: 26187
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?
Upvotes: 24
Views: 38739
Reputation: 11
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
Upvotes: 0
Reputation: 1817
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
...
Upvotes: 37
Reputation: 71
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
}
Upvotes: 5
Reputation: 12300
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
Upvotes: 0