Gathering Timezone Information in GoLang
Tags: go, programming, tutorial, coding
Published: 2023-06-04 17:25:20

A timezone refers to a specific region on Earth that follows the same standard time. Typically, a timezone is identified by a combination of the continent, country, or city within that country. Here are some key details related to timezones:

- Offset from UTC: Represents the time difference in hours and minutes from Coordinated Universal Time (UTC).
- Timezone Abbreviations: Commonly used three-letter abbreviations, such as "IST" (Indian Standard Time) or "EST" (Eastern Standard Time), are associated with timezones.
- Daylight Saving Time (DST): Many timezones observe DST, which involves adjusting the clocks forward by one hour during specific periods.
- Timezone Name: Each timezone is identified by a specific name that represents its location, such as "Asia/Kolkata" or "America/New_York".

In this article, we will explore how to gather the aforementioned details in GoLang. Our focus will be on gathering timezone information on Linux, macOS, and Windows platforms.

## UTC Offset, Abbreviation, and DST

Gathering UTC offset, timezone abbreviation, and DST information in Go is a straightforward process. We can utilize the standard time API to achieve this.

**UTC Offset**

```go
t := time.Now()
utcOffset := t.Format("-07:00")
```
**Abbreviation**

```go
abbr, _ := t.Zone()
```

**DST**

```go
isDst := t.IsDST()
```
Sample values for each of the variables are provided below:

`utcOffset: '+05:30'
abbr: 'IST'
isDst: false`

UTC offsets and abbreviations can be ambiguous without additional context. While UTC offsets indicate the time difference between a local time and Coordinated Universal Time (UTC), they do not provide information about the specific time zone or region.

- Overlapping offsets: Certain time zones or regions can have identical UTC offsets. For instance, both Eastern Standard Time (EST) in the United States and Eastern Standard Time (EST) in Australia share a UTC-3 offset.
- Changes in offsets: UTC offsets can change due to factors such as Daylight Saving Time (DST) or modifications in local time regulations.
- Abbreviation conflicts: Time zone abbreviations are not standardized globally, and multiple regions may use the same abbreviation for different time zones. For example, "IST" can refer to Indian Standard Time or Israel Standard Time.

In the next section, we will explore how to gather the IANA (Internet Assigned Numbers Authority) timezone name, which helps to minimize ambiguity.

## Timezone Name

IANA timezone names consist of two parts: the region and the location. For example, 'America/New_York' represents the timezone of New York City in the United States, and 'Asia/Kolkata' represents the timezone of Kolkata in India.

**Linux and macOS**

The '/etc/localtime' file is a system file commonly found in Unix-like operating systems, including Linux and macOS. It is typically a symbolic link that points to the timezone data file associated with the local timezone. On Linux systems, the '/etc/localtime' symbolic link typically points to an active zone file, such as '/usr/share/zoneinfo/Asia/Kolkata', while on macOS it points to '/var/db/timezone/zoneinfo/Asia/Kolkata'. 

By examining the paths mentioned above, we can determine that the file contains the relevant information about the local timezone, specifically for the Asia/Kolkata timezone.


The following code sample demonstrates reading the timezone information by following the '/etc/localtime' link:

```go
func GetIanaName() (string, error) {
	linkPath := "/etc/localtime"
	targetPath, err := os.Readlink(linkPath)
	if err != nil {
		return "", err
	}

	tzParts := strings.Split(targetPath, "/")
	if len(tzParts) < 3 {
		return "", errors.New("invalid timezone format")
	}

	continent, country := tzParts[len(tzParts)-2], tzParts[len(tzParts)-1]
	timezone := fmt.Sprintf("%s/%s", continent, country)

	_, err = time.LoadLocation(timezone)
	if err != nil {
		return "", err
	}

	return timezone, nil
}
```
**Windows**

The 'systeminfo' command on Windows includes the 'Time Zone' data, as shown in the output below:
`...
Time Zone:    (UTC+05:30) Chennai, Kolkata, Mumbai, New Delhi
...`

We require a mapping between the Windows timezone names and their corresponding IANA timezone names, as illustrated in the snippet below:

```json
{
    ...
    "(UTC+05:30) Chennai, Kolkata, Mumbai, New Delhi": "Asia/Kolkata",
    ...
}
```

Creating this mapping is a manual process, and the [link](https://github.com/sajjaphani/go-timezone/blob/main/timezone/resources/windows2iana.json) contains the reference for the mappings. To establish this mapping, you can find the necessary information by visiting the [link](https://github.com/unicode-org/cldr/blob/main/common/supplemental/windowsZones.xml).

The following snippet demonstrates how to extract the 'Time Zone' value on Windows:

```go
func GetIanaName() (string, error) {
	cmd := exec.Command("systeminfo")

	output, err := cmd.Output()
	if err != nil {
		return "", err
	}

	timezone := parseTimezone(string(output))

	return GetIanaNameForWindowsName(timezone)
}

func parseTimezone(output string) string {
	lines := strings.Split(output, "\n")
	for _, line := range lines {
		if strings.HasPrefix(line, "Time Zone:") {
			parts := strings.Split(line, ":")
			if len(parts) == 2 {
				return strings.TrimSpace(parts[1])
			}
		}
	}

	return ""
}

```

Once we obtain the Windows timezone name, we can utilize the mapping file mentioned earlier to find the corresponding IANA timezone name. The following snippet demonstrates this:

```go
type WindowsZoneMapping struct {
	WindowsTimezone string
	IANATimezone    string
}

func GetIanaNameForWindowsName(windowsName string) (string, error) {
	file, err := Resources.Open("resources/windows2iana.json")
	if err != nil {
		return "", err
	}

	r := bufio.NewReader(file)
	decoder := json.NewDecoder(r)
	var m map[string]string
	err = decoder.Decode(&m)
	if err != nil {
		return "", err
	}

	return m[windowsName], err
}
```

The complete code for this functionality can be found in the GitHub repository [here](https://github.com/sajjaphani/go-timezone).

## Summary

In this article, we have explored timezones and how to retrieve timezone data in Go. We have also examined some of the challenges and ambiguities that can arise when dealing with timezone information. Depending on specific requirements, it may be necessary to use a combination of different timezone values to address these ambiguities effectively.