Reputation: 1272
I'm trying to modify golang timezone for my application
I have took a look at time package, initializing timezone happens in
time/zoneinfo_unix.go @ initLocal
The function simply tries to read environment variable TZ
and if it's valid it loads it
and if it's not it falls back /etc/localtime
and if it's not valid it falls back to UTC
what i have tried so far
1- works fine -But i don't want to use either of those approaches - :
TZ = Africa/Cairo
$ export TZ = Africa/Cairo
2- Didn't work
When i simplify the main and use os.SetEnv("TZ", "Africa/Cairo") without importing any other packages other than "os - time" it works as expected
Any ideas about how to make the second approache work ?
Docker image: golang:1.11.2
Upvotes: 37
Views: 49368
Reputation: 406
Be aware that if you want to deploy your app on AWS time.LoadLocation
may return an error because it can't find database file. From the docs:
LoadLocation looks for the IANA Time Zone database in the following locations in order:
- the directory or uncompressed zip file named by the ZONEINFO environment variable
- on a Unix system, the system standard installation location
- $GOROOT/lib/time/zoneinfo.zip
- the time/tzdata package, if it was imported
You should import time/tzdata
package to resolve the issue:
import _ "time/tzdata" // Important!
func main() {
location, err := time.LoadLocation("Europe/Berlin")
if err != nil {
log.Fatal().Msgf("Err loading location: %v", err)
}
time.Local = location
}
Upvotes: 2
Reputation: 5937
I may be late but setting timezone in a global env is not a reliable approach. It should be set globally in a variable or in a struct. The below is an example of timezone set in a variable. Also in Go Playground
package main
import (
"fmt"
"log"
"time"
)
func main() {
if err := setTimezone("America/Los_Angeles"); err != nil {
log.Fatal(err) // most likely timezone not loaded in Docker OS
}
t := getTime(time.Now())
fmt.Println(t)
}
var loc *time.Location
func setTimezone(tz string) error {
location, err := time.LoadLocation(tz)
if err != nil {
return err
}
loc = location
return nil
}
func getTime(t time.Time) time.Time {
return t.In(loc)
}
Upvotes: 2
Reputation: 409
Adding my anser here for people who stumbled on this page.
There's a global variable in time
package, use it like this in main.go
package main
import "time"
func main() {
loc, err := time.LoadLocation("Africa/Cairo")
// handle err
time.Local = loc // -> this is setting the global timezone
}
Your system must have timezone database installed.
In docker, you must apt get/apk add tzdata
. But if you're using go1.15, you can also embed the timezone database without installing tzdata
on system.
package main
import (
"time"
_ "time/tzdata"
)
func main() {
loc, err := time.LoadLocation("Africa/Cairo")
// handle err
time.Local = loc // -> this is setting the global timezone
}
Upvotes: 37
Reputation: 418745
You can achieve what you want from inside your app using os.Setenv("TZ", "Africa/Cairo")
, what matters is that you must call this before any other package uses anything from the time
package.
How to ensure that? Create a package that does nothing else except sets the timezone (later you may add other things to it, but for our example that's enough).
Like this:
package tzinit
import (
"os"
)
func init() {
os.Setenv("TZ", "Africa/Cairo")
}
Import this tzinit
package first thing in your main
package like this:
package main
import _ "path/to/tzinit"
// Your other, "regular" imports:
import (
"fmt"
"os"
"time"
...
)
And so setting the TZ
env var will happen before any other package could access the time
package.
Note that I used a separate import
declaration just for tzinit
, and the reason for this is because many code editors / IDEs will rearrange your imports alphabetically, this will ensure that importing tzinit
will remain the first import.
A word of warning.
The Spec: Package initialization states the requirements and rules of initializing packages, and the order in which imports are processed is not specified (only thing guaranteed is that all referenced package will be initialized recursively before it can be used). This means that although current compilers process them as listed, you cannot rely on this for 100%. There's also the issue of having multiple source files even for the main
package, supplying them in different order to the compiler may also change the initialization order. The spec has this as a "recommendation":
To ensure reproducible initialization behavior, build systems are encouraged to present multiple files belonging to the same package in lexical file name order to a compiler.
So to be on the safe side, best would be to set the TZ
environment variable before the Go app is launched.
Upvotes: 41