Get complete UTC offset format - go

I need to get the UTC offset for a location. I am getting trouble with the inconsistency of the results from different values. All I need to get are values in the format +HHMM (e.g., +0100 for "Europe/Rome").
func main() {
loc, _:= time.LoadLocation("Asia/Kathmandu")
offset, others:= time.Now().In(loc).Zone()
fmt.Println(offset, others)
}
Playground
What I get:
"Asia/Kathmandu": +0545 (suitable)
"Asia/Ho_Chi_Minh": +07 (should be +0700)
"America/Phoenix": MST (should be -0700)
"Europe/Rome": CET (should be +0100)
Reference Timezone country names

The Zone() method you're using is working exactly as advertized.
Zone computes the time zone in effect at time t, returning the abbreviated name of the zone (such as "CET") and its offset in seconds east of UTC.
A better approach for you would be to use the Format method. Something like:
zone := time.Now().In(loc).Format("-0700")
Of course, be aware: Even this won't be 100% consistent, due to daylight savings time.

Related

Understanding tz offset discrepancy on different systems

Hoping someone can see what I'm doing wrong, or misunderstanding.
I was using the time pkg in the Go Sandbox, to become familiar with how to use the timezone specific functions correctly.
Even though I already knew some of the offsets, I used the following site to double check myself : https://documentation.mersive.com/content/topics/api-timezones.htm.
In this case, when I ask for the offset for "America/New_York", I expect -14400.
However, when I ran the following on the Go Sandbox, I get -18000 instead:
https://play.golang.org/p/aU0JFHzueU1
americatz, err := time.LoadLocation("America/New_York")
if err != nil {
fmt.Println(err)
return
}
t := time.Now().In(americatz)
zone, offset := t.Zone()
fmt.Printf("%v :: %v\n", zone, offset)
I noticed that when I brought the same code over to a linux machine, it did exactly what I expected. I'm nervous, because I don't have a firm understanding why the two output's for a very common tz would be different.
I know enough that LoadLocation looks for a zipfile from the ZONEINFO env var. Otherwise it'll look in other system places like the $GOROOT/lib/time/zoneinfo.zip.
Is it really just plain and simple that the Go Server the code runs on vs the linux server have different zone info files? And if I want the exact same behavior across all systems... does this mean I need to always load in and set my own ZONEINFO variable? I feel like most people would expect -14400...
Thanks.
In the playground the time is set to "2009-11-10 23:00:00 UTC" because "This makes it easier to cache programs by giving them deterministic output." (from the 'About' box).
This can have an impact on the timezone offset due to daylight savings. The following will give the answer you are expecting:
today := time.Date(2020,10,16,0,0,0,0,americatz)
zone, offset = today.Zone()
fmt.Printf("%v :: %v\n", zone, offset)
Playground
Further detail:
Due to daylight savings the offset changes depending upon the time; for example:
time.Date(2020,10,16,0,0,0,0,americatz).Zone() will return an offest of -14400 whereas time.Date(2020,1,16,0,0,0,0,americatz).Zone() would return -18000.
It just so happens that the time that now() returns in the playground (2009-11-10) is EST (daylight savings ended Sunday, 1 November 2009, 2:00 a.m.) rather than EDT. If you run your test again in a month you will not see this difference because both times will be in EST.

Why running Golang's time.Now() at OpenWRT always get UTC time?

I wrote a Golang program that runs on OpenWRT.
package main
import (
"fmt"
"time"
)
func main(){
fmt.Println(time.Now())
}
When I run this program on my Macbook, I always get the correct local time.
However, when running this program on OpenWRT, I always get UTC time.
I have set the time zone and time of OpenWRT. When I execute uci show system, I can see the right time zone. When I execute date the right local time can be display correctly.
So my question is, how do I get the correct local time using Golang's time.Now() on OpenWRT?
The root of my problem is that my OpenWRT lacks the zoneinfo package. So I run opkg update && opkg install zoneinfo-xxx first.
This is part of go/src/time/zoneinfo_unix.go:
func initLocal() {
// consult $TZ to find the time zone to use.
// no $TZ means use the system default /etc/localtime.
// $TZ="" means use UTC.
// $TZ="foo" means use /usr/share/zoneinfo/foo.
...
}
According to this file, if there is no "TZ" variable, Golang will use the time zone pointed to by /etc/localtime when calling time.Now().
Then I set time zone at /etc/config/system (option zonename 'xxxxxx') and run /etc/init.d/system restart. Finally, I can get the correct time.Now().
OpenWRT stores the time zone inside a file named /etc/TZ. If this file is missing or empty, OpenWRT assumes the local time equals UTC time. Source
how do I get the correct local time using Golang's time.Now() on
OpenWRT?
Specifying the Time Zone with TZ
The is the value you must add to or substract from the local time to get the UTC time. This offset will be positive if the local time zone is west of the Prime Meridian and negative if it is east.

Why is a different timezone being printed with ParseInLocation?

I'm trying to parse a string into time with a user-specific timezone location -
// error handling skipped for brevity
loc, _ := time.LoadLocation("Asia/Kolkata")
now, _ := time.ParseInLocation("15:04", "10:10", loc)
fmt.Println("Location : ", loc, " Time : ", now)
The output I get on my system is - Location : Asia/Kolkata Time : 0000-01-01 10:10:00 +0553 HMT
Where did this HMT time zone come from?
If instead of parsing the time I use now := time.Now().In(loc), the timezone printed is correct - IST. Am I doing something wrong with timezone parsng or is my system timezone database faulty?
This may be a relic of the fact that your year for now is 0000, while time.Now() returns the current time. Timezones are weird, and certain locations haven't always used the same timezone. This is an excerpt from the IANA Time Zone Database:
# Zone NAME GMTOFF RULES FORMAT [UNTIL]
Zone Asia/Kolkata 5:53:28 - LMT 1854 Jun 28 # Kolkata
5:53:20 - HMT 1870 # Howrah Mean Time?
5:21:10 - MMT 1906 Jan 1 # Madras local time
5:30 - IST 1941 Oct
5:30 1:00 +0630 1942 May 15
5:30 - IST 1942 Sep
5:30 1:00 +0630 1945 Oct 15
5:30 - IST
If I am interpreting this correctly, it seems HMT was used from 1854 until 1870—I'm not exactly sure why this would cause it to be used for year 0000, which would seem to fall under LMT, but it's possible the Go database is slightly different (or it's possible that I'm misinterpreting the database). If you're concerned about the correct timezone being used for historical dates (like 0000) I'm not sure I can give a great answer, however for anything recent IST should be correctly used.

How to convert between timezones in Go? [duplicate]

This question already has answers here:
Convert UTC to "local" time in Go
(3 answers)
Closed 7 years ago.
For example time.Now() has a timezone of UTC.
utcNow := time.Now()
fmt.Println(utcNow)
Outputs
2009-11-10 23:00:00 +0000 UTC
How do I convert this time to Japan Standard time?
It looks like you're running that in the Go playground, which is why the time is automatically set to UTC (it's also always set to November 2009 when a program is launched).
If you run time.Now() on your own machine, it should pick up the local region. Alternatively, if you want to force the time to be in a specific timezone, you can use a time.Location object along with the time.Time.In function.
l, err := time.LoadLocation("Asia/Tokyo") // Look up a location by it's IANA name.
if err != nil {
panic(err) // You can handle this gracefully.
}
fmt.Println(utcNow.In(l))
Note that it's still showing the same moment in time, but now with JST's offset.
For more information, look at the go documentation for the time package. http://golang.org/pkg/time

mktime issue when converting a struct tm that doesn't exist (because of the daylight changing time)

I have a mktime issue, if the hour does not exist because of DST change, then mktime on windows using MSVS 2010 will return a time_t in the past, in my case for 23:00, when it should return 1:00AM next day (on Linux it is returning 1:00AM as it should). My issue is happening on Brazil timezone (GMT -3) exactly when the auto adjust daylight saving time should happen. In their case this is happening on 21.Oct.2012 at 0:00 AM (this will become 1:00 AM).
This is a part of the code:
/* test_date1.cpp : Defines the entry point for the console application.
*
*/
#include "StdAfx.h"
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <time.h>
int _tmain(int argc, _TCHAR* argv[])
{
time_t mytime=1350784881;
struct tm *timeinfo;
char *tz;
/*time ( &mytime ); */
timeinfo = localtime ( &mytime );
printf("%.2d/%.2d/%.4d, %.2d:%.2d isdst?=%d\n",
timeinfo->tm_mday, timeinfo->tm_mon, timeinfo->tm_year, timeinfo->tm_hour, timeinfo-> tm_min, timeinfo -> tm_isdst);
timeinfo->tm_mday=21;
timeinfo->tm_mon=9;
timeinfo->tm_year=112;
timeinfo->tm_hour=0;
timeinfo->tm_min=0;
timeinfo->tm_isdst=-1;
printf("The shit: %.2d/%.2d/%.4d, %.2d:%.2d isdst?=%d\n",
timeinfo->tm_mday, timeinfo->tm_mon, timeinfo->tm_year, timeinfo->tm_hour, timeinfo-> tm_min, timeinfo -> tm_isdst);
mytime= mktime(timeinfo);
printf("mytime is=%d\n", mytime);
timeinfo = localtime ( &mytime );
printf("%.2d/%.2d/%.4d, %.2d:%.2d isdst?=%d\n",
timeinfo->tm_mday, timeinfo->tm_mon, timeinfo->tm_year, timeinfo->tm_hour, timeinfo-> tm_min, timeinfo -> tm_isdst);
return 0;
}
The results on Windows are:
20/09/0112, 23:01 isdst?=0
21/09/0112, 00:00 isdst?=-1
mytime is=1350784800
20/09/0112, 23:00 isdst?=0
And on Linux are:
20/09/0112, 23:01 isdst?=0
21/09/0112, 00:00 isdst?=-1
mytime is=1350788400
21/09/0112, 01:00 isdst?=1
As you can see mytime diff is 3600 seconds between what time_t returns the C from Unix and what returns the C from Microsoft Visual Studio 2010.
This program should run on different platforms (UNIX/WINDOWS/etc) and on any timezone, so I should not hardcode the timezone.
As you saw the problem is on Windows where the time is returned wrongly. In this moment I don't know how to fix this issue. Did someone had this particular problem? How did you solved it? I specifically need the beginging of the Local Day.
Thanks a lot,
Jokerush
I don't know if the behaviour of mktime is defined when you pass it an invalid time, so this might not actually be a bug.
At any rate, short of re-implementing mktime yourself, you'll need to work around the problem. I propose the following algorithm:
Build a struct tm for 9am on the day in question.
Convert to time_t.
Subtract 24 hours from the time_t.
If there's no daylight savings change, then at this point you're looking at 9am the previous day. If daylight savings started, you're looking at 8am; if it finished, you're looking at 10am.
Convert back to a struct tm.
Compare the date in the struct tm to the original date.
If it isn't the right day yet, add an hour to the time_t and try again.
Since you're only adding an hour at a time, once you've got the date you wanted, you can be sure you've got the earliest time on that date.
I'm making a few assumptions that I think are currently safe: that daylight savings time never starts or finishes part way through an hour, that it never starts or finishes between 8am and 10am, and that it never changes by more than an hour.
I'm intentionally not assuming that daylight savings never starts or finishes in the evening (e.g., 11pm becomes midnight or vice-versa) or in the early morning (e.g., 2am becomes 3am or vice-versa, which as it happens is exactly what happens here).

Resources