talk about time zones

Original link: https://articles.singee.me/timezone

bafkreif3e4h5fzpve6upyaor33gagp7kqi3um2c

Usually, the problem of time zone conversion is often involved in localization, and usually the “default” time zone we use is UTC or “local” before we really pay attention to the time zone.

This article takes Go as an example to analyze the time zone usage in Go.

read time zone

In Go, LoadLocation function is used to read the time zone.

 1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
 // LoadLocation returns the Location with the given name.
//
// If the name is "" or "UTC", LoadLocation returns UTC.
// If the name is "Local", LoadLocation returns Local.
//
// Otherwise, the name is taken to be a location name corresponding to a file
// in the IANA Time Zone database, such as "America/New_York".
//
// 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
func LoadLocation (name string ) (*Location, error )

Read the comments to know that if the name is empty/UTC, UTC is used, and if it is Local, the local time zone is used (explained later), otherwise, it is read from a specific location.

The so-called read refers to the read tzfile time zone file, you can read this document for more information. To put it simply, a time zone file is a binary file starting with TZif , which contains information such as time zone offset, leap second, daylight saving time, etc. Go can read and parse related files.

  1. If there is a ZONEINFO environment variable, use the directory/compressed file pointed to by the variable to read
  2. On Unix systems, use the system standard location
  3. (mainly used when compiling Go) read from $GOROOT/lib/time/zoneinfo.zip
  4. (if time/tzdata is imported) read from program embedded data

We are more concerned about 2, which is the storage location of Unix standard time zone files. In Unix systems, time zone files are usually stored in the /usr/share/zoneinfo/ directory (or /usr/share/lib/zoneinfo/ 或/usr/lib/locale/TZ/ depending on the system), for example , the time zone definition file of China ( Asia/Shanghai ) is /usr/share/zoneinfo/Asia/Shanghai . Therefore, usually the program can obtain the time zone information directly from the system.

Note that in the alpine environment, there is no time zone definition file, so we need to pay special attention to processing

  1. You can use import _ "time/tzdata" in the program to compile the time zone file into the program at compile time, so that you can also find the standard IANA time zone definition when you cannot find the time zone definition in the system
  2. If we don’t need a particularly dynamic time zone, we can avoid using LoadLocation but use FixedZone to provide the time zone name and offset by ourselves, for example, for China UTF+8, you can use time.FixedZone("Asia/Shanghai", 8*60*60)

local time zone

Usually, the time zone we use “by default” is the so-called “local time zone” before we really consider the time zone issue.

Take time.Now as an example,

 1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
 type Time struct {
wall uint64
ext int64

loc *Location
}

// Now returns the current local time.
func Now () Time {
sec, nsec, mono := now()
mono -= startNano
sec += unixToInternal - minWall
if uint64 (sec)>> 33 != 0 {
// Seconds field overflowed the 33 bits available when
// storing a monotonic time. This will be true after
//March 16, 2157.
return Time{ uint64 (nsec), sec + minWall, Local}
}
return Time{hasMonotonic | uint64 (sec)<<nsecShift | uint64 (nsec), mono, Local}
}

It can be seen that the last field loc *Location of Time structure is the time zone, and the time zone used in time.Now is Local .

We’ve focused on time zones in this article, but if you’re interested in other factors in this code, feel free to read Do You Really Know Time.Now()? .

Local here is the local time zone, that is, the time zone of the machine where the program is running.

 1
2
3
4
5
6
7
 // Local represents the system's local time zone.
// On Unix systems, Local consults the TZ environment
// variable to find the time zone to use. No TZ means
// use the system default /etc/localtime.
// TZ="" means use UTC.
// TZ="foo" means use file foo in the system timezone directory.
var Local *Location = &localLoc

Reading the description about Local in Go shows that Go will respect the time zone specified by TZ environment variable first. If there is no special specification, use the /etc/localtime file to read the current time zone.

So, how is Local initialized?

 1
2
3
4
5
6
7
8
9
10
11
12
13
14
 // localLoc is separate so that initLocal can initialize
// it even if a client has changed Local.
var localLoc Location
var localOnce sync.Once

func (l *Location) get() *Location {
if l == nil {
return &utcLoc
}
if l == &localLoc {
localOnce. Do(initLocal)
}
return l
}

From the logic of this code, it is not difficult to guess that Local does not actually read the above information when the program starts, but is actually initialized by executing initLocal function when it is used for the first time. At the same time, this code also implicitly puts forward a requirement for using Location: the get method must be called to obtain the “real Location”.

initLocal function is defined in zoneinfo_*.go and has different implementations on different machines, but essentially

The colon is ignored if the TZ content starts : :

  1. If the TZ environment variable is not specified, read /etc/localtime (usually a soft link pointing to the real time zone file)
  2. If the specified TZ environment variable is an absolute path, read the file
  3. Otherwise, read the time zone file according to the LoadLocation process analyzed above

In addition, if the above 3 steps fail, it will fallback to use UTC time

Extra meal: tzdata

tzdata defines the changes of the historical time zone in detail, including daylight saving time, leap second, etc. Therefore, Asia/Shanghai is more versatile than the simple GMT+8 and can correctly process historical data.

If you are interested, you can use zdump Asia/Shanghai -i to view the time zone changes in Shanghai and compare it with zdump America/Chicago -i when using daylight saving time.

This article is transferred from: https://articles.singee.me/timezone
This site is only for collection, and the copyright belongs to the original author.