How to parse time using a specific timezone - go

I am to get a time struct from a string. I am using the function time.ParseTime() with the layout "2006-01-02 15:04".
When I execute the function with any valid time string I get a time struct pointing to that time stamp but it is in UTC.
How can I change it to a different time zone? To be clear I want the same timestamp but with a different time zone. I don't want to convert between timezones; I just want to get the same time object but not in UTC.

Use time.ParseInLocation to parse time in a given Location when there's no time zone given. time.Local is your local time zone, pass that in as your Location.
package main
import (
"fmt"
"time"
)
func main() {
// This will honor the given time zone.
// 2012-07-09 05:02:00 +0000 CEST
const formWithZone = "Jan 2, 2006 at 3:04pm (MST)"
t, _ := time.ParseInLocation(formWithZone, "Jul 9, 2012 at 5:02am (CEST)", time.Local)
fmt.Println(t)
// Lacking a time zone, it will use your local time zone.
// Mine is PDT: 2012-07-09 05:02:00 -0700 PDT
const formWithoutZone = "Jan 2, 2006 at 3:04pm"
t, _ = time.ParseInLocation(formWithoutZone, "Jul 9, 2012 at 5:02am", time.Local)
fmt.Println(t)
}

Related

Given the IANA entry is known, how do I find a specific dates offset (daylight vs standard)?

I have a list of date strings that appear in this format without any time zone or offset information:
[
"2019-04-30T12:34:00.000", // In 2019, DST started in March 10, 2019, so this should have the appropriate DST offset
"2017-11-20T13:45:00.000" // In 2017, DST ended on November 5, 2017 so this should have the appropriate standard time offset
]
I know the IANA region (eg, America/New_York) that these dates and times were created in, but I cannot figure out how to dynamically generate the appropriate offset given this information using go and the time package.
I have thought about the following:
Appending a hardcoded value to the end of the date string (ie, "2019-04-30T12:34:00.000" + "-04:00)
Write custom logic to determine if a date falls within the boundary of standard or daylight savings time
However, these solutions only work for some dates or the logic becomes exceedingly complicated.
I was able to figure it out
package main
import (
"log"
"time"
)
func main() {
Chicago, _ := time.LoadLocation("America/Chicago")
t := time.Date(2019, time.March, 1, 12, 30, 0, 0, Chicago)
log.Print(t) // 2019-03-01 12:30:00 -0600 CST
log.Print(t.UTC()) // 2019-03-01 18:30:00 +0000 UTC
t = time.Date(2019, time.November, 2, 12, 30, 0, 0, Chicago)
log.Print(t) // 2019-11-02 12:30:00 -0500 CDT
log.Print(t.UTC()) // 2019-11-02 17:30:00 +0000 UTC
}
go playground # https://play.golang.org/p/nP28y9jSDAk
An even cleaner solution by leveraging a custom layout and time.LoadLocation
package main
import (
"fmt"
"time"
)
func main() {
Chicago, _ := time.LoadLocation("America/Chicago")
cdt, _ := time.ParseInLocation("2006-01-02T15:04:05.999999", "2019-04-30T12:34:00.000", Chicago)
fmt.Println(cdt)
fmt.Println(cdt.UTC())
cst, _ := time.ParseInLocation("2006-01-02T15:04:05.999999", "2017-11-20T13:45:00.000", Chicago)
fmt.Println(cst)
fmt.Println(cst.UTC())
}
go playground # https://play.golang.org/p/3Ai4qVz0af5

How to check whether current local time is DST?

In Ruby, for example, there's the Time#dst? function, which returns true in the case it is daylight saving time. Is there a Go standard library API call to do the same?
In August 2021 go 1.17 was released which now adds the time.Time method IsDST:
IsDST reports whether the time in the configured location is in
Daylight Savings Time.
The Location api doesn't export the DST value of the timezone. This was brought up in the golang-nuts forum several years ago. One suggestion is to compare the January 1 timezone offset to the July 1 timezone offset. A working solution of this was posted using this method. One caveat is that goplay has the wrong local time, so it doesn't correctly report the information if you run it there. You can run it locally to verify that it does work.
Another way would be to use reflection via the reflect package. A solution that I wrote to do this is available here. There are a lot of problems with this method.
Edit: Really it should probably use cacheZone but does a linear search of the zones to find one that matches. This can lead to errors because some timezones share name and offset. The correct way would be to look at cacheZone and use that if it is set. Otherwise, you'll need to either look at zoneTrans or at least look at how lookup(int64) is implemented.
You can infer the result. For example,
package main
import (
"fmt"
"time"
)
// isTimeDST returns true if time t occurs within daylight saving time
// for its time zone.
func isTimeDST(t time.Time) bool {
// If the most recent (within the last year) clock change
// was forward then assume the change was from std to dst.
hh, mm, _ := t.UTC().Clock()
tClock := hh*60 + mm
for m := -1; m > -12; m-- {
// assume dst lasts for least one month
hh, mm, _ := t.AddDate(0, m, 0).UTC().Clock()
clock := hh*60 + mm
if clock != tClock {
if clock > tClock {
// std to dst
return true
}
// dst to std
return false
}
}
// assume no dst
return false
}
func main() {
pstLoc, err := time.LoadLocation("America/Los_Angeles")
if err != nil {
fmt.Println(err)
return
}
utc := time.Date(2018, 10, 29, 14, 0, 0, 0, time.UTC)
fmt.Println(utc, utc.Location(), ": DST", isTimeDST(utc))
local := utc.In(time.Local)
fmt.Println(local, local.Location(), ": DST", isTimeDST(local))
pst := utc.In(pstLoc)
fmt.Println(pst, pst.Location(), ": DST", isTimeDST(pst))
utc = utc.AddDate(0, 3, 0)
fmt.Println(utc, utc.Location(), ": DST", isTimeDST(utc))
local = utc.In(time.Local)
fmt.Println(local, local.Location(), ": DST", isTimeDST(local))
pst = utc.In(pstLoc)
fmt.Println(pst, pst.Location(), ": DST", isTimeDST(pst))
}
Output:
2018-10-29 14:00:00 +0000 UTC UTC : DST false
2018-10-29 10:00:00 -0400 EDT Local : DST true
2018-10-29 07:00:00 -0700 PDT America/Los_Angeles : DST true
2019-01-29 14:00:00 +0000 UTC UTC : DST false
2019-01-29 09:00:00 -0500 EST Local : DST false
2019-01-29 06:00:00 -0800 PST America/Los_Angeles : DST false

Golang time - time zone showing twice

On running this code the result should show date time and zone
Surprisingly the result shows time zone twice and am not able to figure out why
package main
import (
"fmt"
"time"
)
func main() {
mytime, _ := time.Parse("02/Jan/2006:15:04:05 -0700", "07/Apr/2017:01:26:05 +0530")
fmt.Println(mytime)
}
Output of this is
2017-04-07 01:26:05 +0530 +0530
So my question is why timezone showing twice ?
The fmt.Println invokes the Time's .String() function that returns the time in the following format:
"2006-01-02 15:04:05.999999999 -0700 MST"
Which as you see contains both the timezone offset and the timezone name.
In your case there is no timezone name known for the time, so it outputs the offset twice.
References:
https://golang.org/pkg/time/#Time.String

How to convert UTC time to unix timestamp

I am looking for an option to convert UTC time string to unix timestamp.
The string variable I have is 02/28/2016 10:03:46 PM and it needs to be converted to a unix timestamp like 1456693426
Any idea how to do that?
First of, the unix timestamp 1456693426 does not have the time 10:03:46 PM but 9:03:46 PM in UTC.
In the time package there is the function Parse with expects a layout to parse the time. The layout is constructed from the reference time Mon Jan 2 15:04:05 -0700 MST 2006. So in your case the layout would be 01/02/2006 3:04:05 PM. After using Parse you get a time.Time struct on which you can call Unix to receive the unix timestamp.
package main
import (
"fmt"
"time"
)
func main() {
layout := "01/02/2006 3:04:05 PM"
t, err := time.Parse(layout, "02/28/2016 9:03:46 PM")
if err != nil {
fmt.Println(err)
}
fmt.Println(t.Unix())
}

How can I extract the value of my current local time offset?

I'm struggling a bit trying to format and display some IBM mainframe TOD clock data. I want to format the data in both GMT and local time (as the default -- otherwise in the zone the user specifies).
For this, I need to get the value of the local time offset from GMT as a signed integer number of seconds.
In zoneinfo.go (which I confess I don't fully understand), I can see
// A zone represents a single time zone such as CEST or CET.
type zone struct {
name string // abbreviated name, "CET"
offset int // seconds east of UTC
isDST bool // is this zone Daylight Savings Time?
}
but this is not, I think, exported, so this code doesn't work:
package main
import ( "time"; "fmt" )
func main() {
l, _ := time.LoadLocation("Local")
fmt.Printf("%v\n", l.zone.offset)
}
Is there a simple way to get this information?
You can use the Zone() method on the time type:
package main
import (
"fmt"
"time"
)
func main() {
t := time.Now()
zone, offset := t.Zone()
fmt.Println(zone, offset)
}
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.
Package time
func (Time) Local
func (t Time) Local() Time
Local returns t with the location set to local time.
func (Time) Zone
func (t Time) Zone() (name string, offset int)
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.
type Location
type Location struct {
// contains filtered or unexported fields
}
A Location maps time instants to the zone in use at that time.
Typically, the Location represents the collection of time offsets in
use in a geographical area, such as CEST and CET for central Europe.
var Local *Location = &localLoc
Local represents the system's local time zone.
var UTC *Location = &utcLoc
UTC represents Universal Coordinated Time (UTC).
func (Time) In
func (t Time) In(loc *Location) Time
In returns t with the location information set to loc.
In panics if loc is nil.
For example,
package main
import (
"fmt"
"time"
)
func main() {
t := time.Now()
// For a time t, offset in seconds east of UTC (GMT)
_, offset := t.Local().Zone()
fmt.Println(offset)
// For a time t, format and display as UTC (GMT) and local times.
fmt.Println(t.In(time.UTC))
fmt.Println(t.In(time.Local))
}
Output:
-18000
2016-01-24 16:48:32.852638798 +0000 UTC
2016-01-24 11:48:32.852638798 -0500 EST
I don't think it makes sense to manually convert time to another TZ. Use time.Time.In function:
package main
import (
"fmt"
"time"
)
func printTime(t time.Time) {
zone, offset := t.Zone()
fmt.Println(t.Format(time.Kitchen), "Zone:", zone, "Offset UTC:", offset)
}
func main() {
printTime(time.Now())
printTime(time.Now().UTC())
loc, _ := time.LoadLocation("America/New_York")
printTime(time.Now().In(loc))
}

Resources