Parsing a UTC ISO8601 time format in Golang - go

I'm being sent a date in the following format:
2021-05-09T12:10:00+01:00
Which is apparently a valid date format: https://en.wikipedia.org/wiki/ISO_8601
I'm attempting to parse that date in Go:
pt, err := time.Parse("2006-01-02T15:04:05+00:00", dt)
I've also tried to use time.RFC3339
But neither seem to pick up the timezone. In this case I get:
2021/05/10 21:02:02 http: panic serving [::1]:62125: parsing time "2021-05-09T12:10:00 01:00" as "2006-01-02T15:04:05+00:00": cannot parse " 01:00" as "+00:00"

The problem is your layout parameter,
"2006-01-02T15:04:05+00:00"
Instead of +00:00 you should have -07:00
This should help,
package main
import (
"fmt"
"time"
)
func main() {
date := "2021-05-09T12:10:00+01:00"
layout := "2006-01-02T15:04:05-07:00"
t, err := time.Parse(layout, date)
if err != nil {
fmt.Println(err)
}
fmt.Println(t)
}
Output: 2021-05-09 12:10:00 +0100 +0100
Playground: https://play.golang.org/p/UcrIDfJRcNV
Don't get confused as to why the timezone is showing up twice.
It's explained in this answer,
Golang time - time zone showing twice
The special layout parameter only accepts a certain set of valid numbers.
You can refer them here,
https://yourbasic.org/golang/format-parse-string-time-date-example/

You need to relay the timezone via the number -7:00 (not +1:00):
// pt, err := time.Parse("2006-01-02T15:04:05+1:00", dt) // not this
pt, err := time.Parse("2006-01-02T15:04:05-07:00", dt) // this
https://play.golang.org/p/n697vKUHSjD

Related

Convert timestamp and timezone into RFC3339 format

I am taking timestamp from the user like this
2015-05-28T17:00:00
And a timezone "America/Los_Angeles"
Now I want convert the date into something like
2015-05-28T17:00:00-07:00
Is that possible in go ,Please help me out in this ,if you have any links which you can share
You can use ParseInLocation to parse datetime in specific location.
package main
import (
"fmt"
"time"
)
func main() {
loc, err := time.LoadLocation("America/Los_Angeles")
if err != nil {
panic(err)
}
// Note: without explicit zone, returns time in given location.
const shortForm = "2015-05-28T17:00:00"
t, err := time.ParseInLocation("2006-01-02T15:04:05", shortForm, loc)
if err != nil {
panic(err)
}
fmt.Println(t)
}
Its output is:
2015-05-28 17:00:00 -0700 PDT
"timezone" translates to time.Location in go. To load a location by name:
loc, err := time.LoadLocation("America/Los_Angeles")
Parsing:
to interpret the string as "that timestamp in that location":
t, err := time.ParseInLocation("2006-01-02T15:04:05", input, loc)
to interpret the string as "that timestamp in UTC":
t, err := time.Parse("2006-01-02T15:04:05", input)
Formatting:
to format t according to RFC3339 :
fmt.Println(t.Format(time.RFC3339))
t carries its own time.Location, you can also translate that timestamp to the timezone you see fit:
fmt.Println(t.In(loc).Format(time.RFC3339))
fmt.Println(t.UTC().Format(time.RFC3339))
https://go.dev/play/p/g2BgfdYGxU_I

time.Parse offset parsing fails with "cannot parse <x>00 as -0700"

I wish to convert a string timestamp (for which no timezone was provided) to a time with timezone of UTC -08:00.
Code:
package main
import (
"fmt"
"log"
"time"
)
func main() {
layout := "1/02/2006 15:04:05 -700"
cellContent := "7/28/2021 22:45:34"
t, err := time.Parse(layout, fmt.Sprintf("%s %s", cellContent, "-800"))
if err == nil {
fmt.Println(t.String())
} else {
log.Fatal(err)
}
}
This fails with message:
parsing time "7/28/2021 22:45:34 -800" as "1/02/2006 15:04:05 -700":
cannot parse "800" as " -700"
I believe I have an error in my layout string, but haven't been able to identify it. What am I doing wrong?
Go Playground
See comment from #Adrian, who nailed it.
The layout timezone must have a leading zero. Thanks!

How to convert a date with timezone to Javascript ISO format?

I am trying to convert this date string ("2018-10-29T11:48:09.180022-04:00") to ISO format in Go. But not able to do. Can anyone help?
package main
import (
"fmt"
"time"
)
func main() {
l,_ := time.Parse("2006-01-02T15:04:05Z07:00", "2018-10-29T15:18:20-04:00")
fmt.Println(l, time.Now(), time.Now().UTC().Format("2006-01-02T15:04:05Z07:00"))
}
Output:
2018-10-29 15:18:20 -0400 -0400 2009-11-10 23:00:00 +0000 UTC m=+0.000000001 2009-11-10T23:00:00Z
https://play.golang.org/p/gXw39_Y-hpR
Note that your input string is valid ISO 8601 format.
However, for JSON serialization, JavaScript uses a slightly different (but still completely valid) style of ISO 8601 date format in which only 3 digits are used for fractional seconds (giving millisecond resolution) and the timezone is adjusted to Coordinated Universal Time (UTC), (aka GMT+0, or "Zulu" timezone) designated with a Z.
// JavaScript
JSON.stringify(new Date()); // => "2018-10-30T15:22:30.293Z"
// Millisecond resolution ─────────────────────────────┺┻┛┃
// "Zulu" (UTC) time zone ────────────────────────────────┚
You can convert your timestamp into the JavaScript style by first parsing the input string, then converting to Zulu time via the UTC() method, then formatting with the desired output format.
For example (Go Playground):
const (
INPUT_FORMAT = "2006-01-02T15:04:05.999999999-07:00"
OUTPUT_FORMAT = "2006-01-02T15:04:05.000Z"
)
func timestampToJavaScriptISO(s string) (string, error) {
t, err := time.Parse(INPUT_FORMAT, s)
if err != nil {
return "", err
}
return t.UTC().Format(OUTPUT_FORMAT), nil
}
func main() {
s := "2018-10-29T11:48:09.180022-04:00"
s2, err := timestampToJavaScriptISO(s)
if err != nil {
panic(err)
}
fmt.Println(s2)
// 2018-10-29T15:48:09.180Z
}
you can just convert it by using the RFC3339 format:
unitTimeInRFC3339 :=time.Now().Format(time.RFC3339)

Hour out of range on time.parse in golang

I am trying to parse a date time string in go. I pass the exact string as the format and get and error parsing time "01/31/2000 12:59 AM": hour out of range.
I am getting that string from an input. How can I make this work?
Here is the code (https://play.golang.org/p/Kg9KfFpU2z)
func main() {
layout := "01/31/2000 12:59 AM"
if t, err := time.Parse(layout, "01/31/2000 12:59 AM"); err == nil {
fmt.Println("Time decoded:", t)
} else {
fmt.Println("Failed to decode time:", err)
}
}
Based on your shared code, you should change the layout to 01/02/2006 03:04 AM to fix it:
Note:
If you have 24 hours format, you should change the hour part in layout to 15 instead of 03 and also to get rid of AM part e.g. 01/02/2006 15:04
package main
import (
"fmt"
"time"
)
func main() {
layout := "01/02/2006 03:04 AM"
if t, err := time.Parse(layout, "01/31/2000 12:59 AM"); err == nil {
fmt.Println("Time decoded:", t)
} else {
fmt.Println("Failed to decode time:", err)
}
}
Here is a good article that would help you to understand different layouts.
Your format needs to use a very specific date and time, see the docs:
https://golang.org/pkg/time/#example_Parse
Parse parses a formatted string and returns the time value it
represents. The layout defines the format by showing how the reference
time, defined to be
Mon Jan 2 15:04:05 -0700 MST 2006
So you need https://play.golang.org/p/c_Xc_R2OHb

Convert UTC to "local" time in Go

How can I convert UTC time to local time?
I've created a map with the UTC difference for all the countries I need the local time. Then I add that difference as duration to the current time (UTC) and print the result hoping that's the local time of that specific country.
For some reasons the result is wrong. For example with Hungary there is one hour difference. Any idea why I'm getting incorrect results?
package main
import "fmt"
import "time"
func main() {
m := make(map[string]string)
m["Hungary"] = "+01.00h"
offSet, err := time.ParseDuration(m["Hungary"])
if err != nil {
panic(err)
}
t := time.Now().UTC().Add(offSet)
nice := t.Format("15:04")
fmt.Println(nice)
}
Keep in mind that the playground has the time set to 2009-11-10 23:00:00 +0000 UTC, so it is working.
The proper way is to use time.LoadLocation though, here's an example:
var countryTz = map[string]string{
"Hungary": "Europe/Budapest",
"Egypt": "Africa/Cairo",
}
func timeIn(name string) time.Time {
loc, err := time.LoadLocation(countryTz[name])
if err != nil {
panic(err)
}
return time.Now().In(loc)
}
func main() {
utc := time.Now().UTC().Format("15:04")
hun := timeIn("Hungary").Format("15:04")
eg := timeIn("Egypt").Format("15:04")
fmt.Println(utc, hun, eg)
}
Your approach is flawed. A country can have several time zones, for example, US and Russia. Because of daylight saving time (DST), a time zone can have more than one time, for example, Hungary. Hungary is UTC +1:00 and is also UTC+2:00 for DST.
For each location that you want the local time for a given UTC time, use the IANA (tzdata) time zone location. For example,
package main
import (
"fmt"
"time"
)
func main() {
utc := time.Now().UTC()
fmt.Println(utc)
local := utc
location, err := time.LoadLocation("Europe/Budapest")
if err == nil {
local = local.In(location)
}
fmt.Println("UTC", utc.Format("15:04"), local.Location(), local.Format("15:04"))
local = utc
location, err = time.LoadLocation("America/Los_Angeles")
if err == nil {
local = local.In(location)
}
fmt.Println("UTC", utc.Format("15:04"), local.Location(), local.Format("15:04"))
}
Output:
2014-08-14 23:57:09.151377514 +0000 UTC
UTC 23:57 Europe/Budapest 01:57
UTC 23:57 America/Los_Angeles 16:57
References:
IANA Time Zone Database
tz database
tz database time zones
Time zone
Time in Hungary
Save yourself the hassle of messing with specific zones, use location "Local". Here's a full and practical example of local and UTC conversion:
package main
import (
"fmt"
"log"
"time"
)
const (
dateTimeFormat = "2006-01-02 15:04 MST"
dateFormat = "2006-01-02"
timeFormat = "15:04"
)
// A full cycle example of receiving local date and time,
// handing off to a database, retrieving as UTC, and formatting as local datetime
// This should be good in *any* timezone
func main() {
// If using a form for entry, I strongly suggest a controlled format input like
// <input type="date" ... > and <input type="time" ... >
locallyEnteredDate := "2017-07-16"
locallyEnteredTime := "14:00"
// Build a time object from received fields (time objects include zone info)
// We are assuming the code is running on a server that is in the same zone as the current user
zone, _ := time.Now().Zone() // get the local zone
dateTimeZ := locallyEnteredDate + " " + locallyEnteredTime + " " + zone
dte, err := time.Parse(dateTimeFormat, dateTimeZ)
if err != nil {
log.Fatal("Error parsing entered datetime", err)
}
fmt.Println("dte:", dte) // dte is a legit time object
// Perhaps we are saving this in a database.
// A good database driver should save the time object as UTC in a time with zone field,
// and return a time object with UTC as zone.
// For the sake of this example, let's assume an object identical to `dte` is returned
// dte := ReceiveFromDatabase()
// Convert received date to local.
// Note the use of the convenient "Local" location https://golang.org/pkg/time/#LoadLocation.
localLoc, err := time.LoadLocation("Local")
if err != nil {
log.Fatal(`Failed to load location "Local"`)
}
localDateTime := dte.In(localLoc)
fmt.Println("Date:", localDateTime.Format(dateFormat))
fmt.Println("Time:", localDateTime.Format(timeFormat))
}

Resources