Error while using time.Parse when timezone and offset are together - date

I have the following code :
package main
import (
"fmt"
"time"
"log"
)
func main() {
date, err := time.Parse("Monday, 2 January 2006 15:04:05 PM MST-07:00" ,"Thursday, 17 August 2020 13:20:00 PM GMT+08:00")
if err != nil {
log.Fatal(err.Error())
}
fmt.Println(date)
}
And it fails with the following error :
2009/11/10 23:00:00 parsing time "Thursday, 17 August 2020 13:20:00 PM
GMT+08:00" as "Monday, 2 January 2006 15:04:05 PM MST-07:00": cannot
parse ":00" as "-07:00"
But it succeeds if I separate MST-07:00 with a space as : "MST -07:00" in both layout sample and actual string.
What am I doing wrong?

GMT times undergo special handling by time.Parse. The signed offset for GMT in the value must be in the range -23 through +23 excluding zero, and may not include a colon. The layout should just specify MST without an offset.
For example:
package main
import (
"fmt"
"log"
"time"
)
func main() {
for _, ts := range []string{
"Thursday, 17 August 2020 13:20:00 PM GMT",
"Thursday, 17 August 2020 13:20:00 PM GMT+2",
"Thursday, 17 August 2020 13:20:00 PM GMT-2",
} {
date, err := time.Parse("Monday, 2 January 2006 15:04:05 PM MST", ts)
if err != nil {
log.Fatal(err.Error())
}
fmt.Println(date)
}
}
yields the output:
crow#mac:tp$ ./example
2020-08-17 13:20:00 +0000 GMT
2020-08-17 15:20:00 +0200 GMT+2
2020-08-17 11:20:00 -0200 GMT-2
An issue was raised for this a while back, and the outcome was (with reference to an example time string containing GMT+10:00):
The special handling of GMT, which is needed for other things, makes
it very difficult to know whether the +10:00 should be considered part
of the time zone or left alone to match the layout.
and so the issue was closed without proposed changes.

Related

How can I parse date and time directly with timezone

I have these codes:
package main
import (
"fmt"
"time"
)
func main() {
tzJakarta, _ := time.LoadLocation("Asia/Jakarta")
stringTime := fmt.Sprintf("05-05-2019 05:11 %s", tzJakarta)
parsedTime, _ := time.Parse("02-01-2006 15:04 MST", stringTime)
fmt.Println(tzJakarta)
fmt.Println(stringTime)
fmt.Println(parsedTime)
}
The output is:
Asia/Jakarta
05-05-2019 05:11 Asia/Jakarta
0001-01-01 00:00:00 +0000 UTC
What I expect is this:
Asia/Jakarta
05-05-2019 05:11 Asia/Jakarta
0001-01-01 00:00:00 +0700 UTC
How can I achieve this? Is this possible at all with Go?
You will never have 0001-01-01 00:00:00 +0700 UTC because you're ignoring errors, 0001-01-01 00:00:00 +0000 UTC is a zero value. Last statement returns an error about parsing, here is a right version.
package main
import (
"fmt"
"time"
)
func main() {
loc, _ := time.LoadLocation("Asia/Jakarta")
// Note: without explicit zone, returns time in given location.
const shortForm = "02-01-2006 15:04"
t, _ := time.ParseInLocation(shortForm, "05-05-2019 05:11", loc)
fmt.Println(t)
}
Output:
2019-05-05 05:11:00 +0700 WIB
(WIB – Western Indonesian Time)
Platground

Convert DD-MON-YY String to Date [duplicate]

I tried parsing the date string "2014-09-12T11:45:26.371Z" in Go. This time format is defined as:
RFC-3339 date-time
ISO-8601 date-time
Code
layout := "2014-09-12T11:45:26.371Z"
str := "2014-11-12T11:45:26.371Z"
t, err := time.Parse(layout , str)
I got this error:
parsing time "2014-11-12T11:47:39.489Z": month out of range
How can I parse this date string?
Use the exact layout numbers described here and a nice blogpost here.
so:
layout := "2006-01-02T15:04:05.000Z"
str := "2014-11-12T11:45:26.371Z"
t, err := time.Parse(layout, str)
if err != nil {
fmt.Println(err)
}
fmt.Println(t)
gives:
>> 2014-11-12 11:45:26.371 +0000 UTC
I know. Mind boggling. Also caught me first time.
Go just doesn't use an abstract syntax for datetime components (YYYY-MM-DD), but these exact numbers (I think the time of the first commit of go Nope, according to this. Does anyone know?).
The layout to use is indeed "2006-01-02T15:04:05.000Z" described in RickyA's answer.
It isn't "the time of the first commit of go", but rather a mnemonic way to remember said layout.
See pkg/time:
The reference time used in the layouts is:
Mon Jan 2 15:04:05 MST 2006
which is Unix time 1136239445.
Since MST is GMT-0700, the reference time can be thought of as
01/02 03:04:05PM '06 -0700
(1,2,3,4,5,6,7, provided you remember that 1 is for the month, and 2 for the day, which is not easy for an European like myself, used to the day-month date format)
As illustrated in "time.parse : why does golang parses the time incorrectly?", that layout (using 1,2,3,4,5,6,7) must be respected exactly.
As answered but to save typing out "2006-01-02T15:04:05.000Z" for the layout, you could use the package's constant RFC3339.
str := "2014-11-12T11:45:26.371Z"
t, err := time.Parse(time.RFC3339, str)
if err != nil {
fmt.Println(err)
}
fmt.Println(t)
https://play.golang.org/p/Dgu2ZvHwTh
This is rather late to the party, and not really saying anything that hasn't been already said in one form or another, mostly through links above, but I wanted to give a TL;DR recap to those with less attention span:
The date and time of the go format string is very important. It's how Go knows which field is which. They are generally 1-9 left to right as follows:
January / Jan / january / jan / 01 / _1 (etc) are for month
02 / _2 are for day of month
15 / 03 / _3 / PM / P / pm /p are for hour & meridian (3pm)
04 / _4 are for minutes
05 / _5 are for seconds
2006 / 06 are for year
-0700 / 07:00 / MST are for timezone
.999999999 / .000000000 etc are for partial seconds (I think the distinction is if trailing zeros are removed)
Mon / Monday are day of the week (which 01-02-2006 actually was),
So, Don't write "01-05-15" as your date format, unless you want "Month-Second-Hour"
(... again, this was basically a summary of above.)
I will suggest using time.RFC3339 constant from time package. You can check other constants from time package.
https://golang.org/pkg/time/#pkg-constants
package main
import (
"fmt"
"time"
)
func main() {
fmt.Println("Time parsing");
dateString := "2014-11-12T11:45:26.371Z"
time1, err := time.Parse(time.RFC3339,dateString);
if err!=nil {
fmt.Println("Error while parsing date :", err);
}
fmt.Println(time1);
}
This might be super late, but this is for people that might stumble on this problem and might want to use external package for parsing date string.
I've tried looking for a libraries and I found this one:
https://github.com/araddon/dateparse
Example from the README:
package main
import (
"flag"
"fmt"
"time"
"github.com/apcera/termtables"
"github.com/araddon/dateparse"
)
var examples = []string{
"May 8, 2009 5:57:51 PM",
"Mon Jan 2 15:04:05 2006",
"Mon Jan 2 15:04:05 MST 2006",
"Mon Jan 02 15:04:05 -0700 2006",
"Monday, 02-Jan-06 15:04:05 MST",
"Mon, 02 Jan 2006 15:04:05 MST",
"Tue, 11 Jul 2017 16:28:13 +0200 (CEST)",
"Mon, 02 Jan 2006 15:04:05 -0700",
"Thu, 4 Jan 2018 17:53:36 +0000",
"Mon Aug 10 15:44:11 UTC+0100 2015",
"Fri Jul 03 2015 18:04:07 GMT+0100 (GMT Daylight Time)",
"12 Feb 2006, 19:17",
"12 Feb 2006 19:17",
"03 February 2013",
"2013-Feb-03",
// mm/dd/yy
"3/31/2014",
"03/31/2014",
"08/21/71",
"8/1/71",
"4/8/2014 22:05",
"04/08/2014 22:05",
"4/8/14 22:05",
"04/2/2014 03:00:51",
"8/8/1965 12:00:00 AM",
"8/8/1965 01:00:01 PM",
"8/8/1965 01:00 PM",
"8/8/1965 1:00 PM",
"8/8/1965 12:00 AM",
"4/02/2014 03:00:51",
"03/19/2012 10:11:59",
"03/19/2012 10:11:59.3186369",
// yyyy/mm/dd
"2014/3/31",
"2014/03/31",
"2014/4/8 22:05",
"2014/04/08 22:05",
"2014/04/2 03:00:51",
"2014/4/02 03:00:51",
"2012/03/19 10:11:59",
"2012/03/19 10:11:59.3186369",
// Chinese
"2014年04月08日",
// yyyy-mm-ddThh
"2006-01-02T15:04:05+0000",
"2009-08-12T22:15:09-07:00",
"2009-08-12T22:15:09",
"2009-08-12T22:15:09Z",
// yyyy-mm-dd hh:mm:ss
"2014-04-26 17:24:37.3186369",
"2012-08-03 18:31:59.257000000",
"2014-04-26 17:24:37.123",
"2013-04-01 22:43",
"2013-04-01 22:43:22",
"2014-12-16 06:20:00 UTC",
"2014-12-16 06:20:00 GMT",
"2014-04-26 05:24:37 PM",
"2014-04-26 13:13:43 +0800",
"2014-04-26 13:13:44 +09:00",
"2012-08-03 18:31:59.257000000 +0000 UTC",
"2015-09-30 18:48:56.35272715 +0000 UTC",
"2015-02-18 00:12:00 +0000 GMT",
"2015-02-18 00:12:00 +0000 UTC",
"2017-07-19 03:21:51+00:00",
"2014-04-26",
"2014-04",
"2014",
"2014-05-11 08:20:13,787",
// mm.dd.yy
"3.31.2014",
"03.31.2014",
"08.21.71",
// yyyymmdd and similar
"20140601",
// unix seconds, ms
"1332151919",
"1384216367189",
}
var (
timezone = ""
)
func main() {
flag.StringVar(&timezone, "timezone", "UTC", "Timezone aka `America/Los_Angeles` formatted time-zone")
flag.Parse()
if timezone != "" {
// NOTE: This is very, very important to understand
// time-parsing in go
loc, err := time.LoadLocation(timezone)
if err != nil {
panic(err.Error())
}
time.Local = loc
}
table := termtables.CreateTable()
table.AddHeaders("Input", "Parsed, and Output as %v")
for _, dateExample := range examples {
t, err := dateparse.ParseLocal(dateExample)
if err != nil {
panic(err.Error())
}
table.AddRow(dateExample, fmt.Sprintf("%v", t))
}
fmt.Println(table.Render())
}
For those of you out there that are encountering this, use the time.RFC3339 versus the string constant of "2006-01-02T15:04:05.000Z". And here is the reason why:
regDate := "2007-10-09T22:50:01.23Z"
layout1 := "2006-01-02T15:04:05.000Z"
t1, err := time.Parse(layout1, regDate)
if err != nil {
fmt.Println("Static format doesn't work")
} else {
fmt.Println(t1)
}
layout2 := time.RFC3339
t2, err := time.Parse(layout2, regDate)
if err != nil {
fmt.Println("RFC format doesn't work") // You shouldn't see this at all
} else {
fmt.Println(t2)
}
This will produce the following result:
Static format doesn't work
2007-10-09 22:50:01.23 +0000 UTC
Here is the Playground Link
If you have worked with time/date formatting/parsing in other languages you might have noticed that the other languages use special placeholders for time/date formatting. For eg ruby language uses
%d for day
%Y for year
etc. Golang, instead of using codes such as above, uses date and time format placeholders that look like date and time only. Go uses standard time, which is:
Mon Jan 2 15:04:05 MST 2006 (MST is GMT-0700)
or
01/02 03:04:05PM '06 -0700
So if you notice Go uses
01 for the day of the month,
02 for the month
03 for hours,
04 for minutes
05 for second
and so on
Therefore for example for parsing 2020-01-29, layout string should be 06-01-02 or 2006-01-02.
You can refer to the full placeholder layout table at this link - https://golangbyexample.com/parse-time-in-golang/

Golang date parsing

I have a string like this
Fri, 3 Mar 2017 13:08:54 +0100
I need to convert this string in a time.Time object in Golang.
The layout seems to be RFC1123Z so I have try in this way
(RFC1123Z = "Mon, 02 Jan 2006 15:04:05 -0700" // RFC1123 with numeric zone)
d := "Thu, 2 Mar 2017 10:44:13 +0100"
da, _ := time.Parse(time.RFC1123Z, d)
fmt.Println(da)
But I get this:
0001-01-01 00:00:00 +0000 UTC
What is wrong?
For it to be time.RFC1123Z the day must be 02 not 2.
package main
import (
"fmt"
"time"
)
func main() {
d := "Thu, 02 Mar 2017 10:44:13 +0100"
da, _ := time.Parse(time.RFC1123Z, d)
fmt.Println(da)
}
https://play.golang.org/p/JFtErfZTtk
If you cannot change the source time string you can alternatively parse it with the Mon, 2 Jan 2006 15:04:05 -0700 format:
da, _ := time.Parse("Mon, 2 Jan 2006 15:04:05 -0700", d)
References:
https://golang.org/pkg/time/#pkg-constants
Try this
func ParseRFC1123Z(date string) (t time.Time, err error) {
t, err = time.Parse(time.RFC1123Z, date)
errOriginal := err
if err != nil {
t, err = time.Parse("Mon, 2 Jan 2006 15:04:05 -0700", date)
}
if err != nil {
err = errOriginal
}
return
}
https://go.dev/play/p/LwYRTBsM4KU

Parsing RFC-3339 / ISO-8601 date-time string in Go

I tried parsing the date string "2014-09-12T11:45:26.371Z" in Go. This time format is defined as:
RFC-3339 date-time
ISO-8601 date-time
Code
layout := "2014-09-12T11:45:26.371Z"
str := "2014-11-12T11:45:26.371Z"
t, err := time.Parse(layout , str)
I got this error:
parsing time "2014-11-12T11:47:39.489Z": month out of range
How can I parse this date string?
Use the exact layout numbers described here and a nice blogpost here.
so:
layout := "2006-01-02T15:04:05.000Z"
str := "2014-11-12T11:45:26.371Z"
t, err := time.Parse(layout, str)
if err != nil {
fmt.Println(err)
}
fmt.Println(t)
gives:
>> 2014-11-12 11:45:26.371 +0000 UTC
I know. Mind boggling. Also caught me first time.
Go just doesn't use an abstract syntax for datetime components (YYYY-MM-DD), but these exact numbers (I think the time of the first commit of go Nope, according to this. Does anyone know?).
The layout to use is indeed "2006-01-02T15:04:05.000Z" described in RickyA's answer.
It isn't "the time of the first commit of go", but rather a mnemonic way to remember said layout.
See pkg/time:
The reference time used in the layouts is:
Mon Jan 2 15:04:05 MST 2006
which is Unix time 1136239445.
Since MST is GMT-0700, the reference time can be thought of as
01/02 03:04:05PM '06 -0700
(1,2,3,4,5,6,7, provided you remember that 1 is for the month, and 2 for the day, which is not easy for an European like myself, used to the day-month date format)
As illustrated in "time.parse : why does golang parses the time incorrectly?", that layout (using 1,2,3,4,5,6,7) must be respected exactly.
As answered but to save typing out "2006-01-02T15:04:05.000Z" for the layout, you could use the package's constant RFC3339.
str := "2014-11-12T11:45:26.371Z"
t, err := time.Parse(time.RFC3339, str)
if err != nil {
fmt.Println(err)
}
fmt.Println(t)
https://play.golang.org/p/Dgu2ZvHwTh
This is rather late to the party, and not really saying anything that hasn't been already said in one form or another, mostly through links above, but I wanted to give a TL;DR recap to those with less attention span:
The date and time of the go format string is very important. It's how Go knows which field is which. They are generally 1-9 left to right as follows:
January / Jan / january / jan / 01 / _1 (etc) are for month
02 / _2 are for day of month
15 / 03 / _3 / PM / P / pm /p are for hour & meridian (3pm)
04 / _4 are for minutes
05 / _5 are for seconds
2006 / 06 are for year
-0700 / 07:00 / MST are for timezone
.999999999 / .000000000 etc are for partial seconds (I think the distinction is if trailing zeros are removed)
Mon / Monday are day of the week (which 01-02-2006 actually was),
So, Don't write "01-05-15" as your date format, unless you want "Month-Second-Hour"
(... again, this was basically a summary of above.)
I will suggest using time.RFC3339 constant from time package. You can check other constants from time package.
https://golang.org/pkg/time/#pkg-constants
package main
import (
"fmt"
"time"
)
func main() {
fmt.Println("Time parsing");
dateString := "2014-11-12T11:45:26.371Z"
time1, err := time.Parse(time.RFC3339,dateString);
if err!=nil {
fmt.Println("Error while parsing date :", err);
}
fmt.Println(time1);
}
This might be super late, but this is for people that might stumble on this problem and might want to use external package for parsing date string.
I've tried looking for a libraries and I found this one:
https://github.com/araddon/dateparse
Example from the README:
package main
import (
"flag"
"fmt"
"time"
"github.com/apcera/termtables"
"github.com/araddon/dateparse"
)
var examples = []string{
"May 8, 2009 5:57:51 PM",
"Mon Jan 2 15:04:05 2006",
"Mon Jan 2 15:04:05 MST 2006",
"Mon Jan 02 15:04:05 -0700 2006",
"Monday, 02-Jan-06 15:04:05 MST",
"Mon, 02 Jan 2006 15:04:05 MST",
"Tue, 11 Jul 2017 16:28:13 +0200 (CEST)",
"Mon, 02 Jan 2006 15:04:05 -0700",
"Thu, 4 Jan 2018 17:53:36 +0000",
"Mon Aug 10 15:44:11 UTC+0100 2015",
"Fri Jul 03 2015 18:04:07 GMT+0100 (GMT Daylight Time)",
"12 Feb 2006, 19:17",
"12 Feb 2006 19:17",
"03 February 2013",
"2013-Feb-03",
// mm/dd/yy
"3/31/2014",
"03/31/2014",
"08/21/71",
"8/1/71",
"4/8/2014 22:05",
"04/08/2014 22:05",
"4/8/14 22:05",
"04/2/2014 03:00:51",
"8/8/1965 12:00:00 AM",
"8/8/1965 01:00:01 PM",
"8/8/1965 01:00 PM",
"8/8/1965 1:00 PM",
"8/8/1965 12:00 AM",
"4/02/2014 03:00:51",
"03/19/2012 10:11:59",
"03/19/2012 10:11:59.3186369",
// yyyy/mm/dd
"2014/3/31",
"2014/03/31",
"2014/4/8 22:05",
"2014/04/08 22:05",
"2014/04/2 03:00:51",
"2014/4/02 03:00:51",
"2012/03/19 10:11:59",
"2012/03/19 10:11:59.3186369",
// Chinese
"2014年04月08日",
// yyyy-mm-ddThh
"2006-01-02T15:04:05+0000",
"2009-08-12T22:15:09-07:00",
"2009-08-12T22:15:09",
"2009-08-12T22:15:09Z",
// yyyy-mm-dd hh:mm:ss
"2014-04-26 17:24:37.3186369",
"2012-08-03 18:31:59.257000000",
"2014-04-26 17:24:37.123",
"2013-04-01 22:43",
"2013-04-01 22:43:22",
"2014-12-16 06:20:00 UTC",
"2014-12-16 06:20:00 GMT",
"2014-04-26 05:24:37 PM",
"2014-04-26 13:13:43 +0800",
"2014-04-26 13:13:44 +09:00",
"2012-08-03 18:31:59.257000000 +0000 UTC",
"2015-09-30 18:48:56.35272715 +0000 UTC",
"2015-02-18 00:12:00 +0000 GMT",
"2015-02-18 00:12:00 +0000 UTC",
"2017-07-19 03:21:51+00:00",
"2014-04-26",
"2014-04",
"2014",
"2014-05-11 08:20:13,787",
// mm.dd.yy
"3.31.2014",
"03.31.2014",
"08.21.71",
// yyyymmdd and similar
"20140601",
// unix seconds, ms
"1332151919",
"1384216367189",
}
var (
timezone = ""
)
func main() {
flag.StringVar(&timezone, "timezone", "UTC", "Timezone aka `America/Los_Angeles` formatted time-zone")
flag.Parse()
if timezone != "" {
// NOTE: This is very, very important to understand
// time-parsing in go
loc, err := time.LoadLocation(timezone)
if err != nil {
panic(err.Error())
}
time.Local = loc
}
table := termtables.CreateTable()
table.AddHeaders("Input", "Parsed, and Output as %v")
for _, dateExample := range examples {
t, err := dateparse.ParseLocal(dateExample)
if err != nil {
panic(err.Error())
}
table.AddRow(dateExample, fmt.Sprintf("%v", t))
}
fmt.Println(table.Render())
}
For those of you out there that are encountering this, use the time.RFC3339 versus the string constant of "2006-01-02T15:04:05.000Z". And here is the reason why:
regDate := "2007-10-09T22:50:01.23Z"
layout1 := "2006-01-02T15:04:05.000Z"
t1, err := time.Parse(layout1, regDate)
if err != nil {
fmt.Println("Static format doesn't work")
} else {
fmt.Println(t1)
}
layout2 := time.RFC3339
t2, err := time.Parse(layout2, regDate)
if err != nil {
fmt.Println("RFC format doesn't work") // You shouldn't see this at all
} else {
fmt.Println(t2)
}
This will produce the following result:
Static format doesn't work
2007-10-09 22:50:01.23 +0000 UTC
Here is the Playground Link
If you have worked with time/date formatting/parsing in other languages you might have noticed that the other languages use special placeholders for time/date formatting. For eg ruby language uses
%d for day
%Y for year
etc. Golang, instead of using codes such as above, uses date and time format placeholders that look like date and time only. Go uses standard time, which is:
Mon Jan 2 15:04:05 MST 2006 (MST is GMT-0700)
or
01/02 03:04:05PM '06 -0700
So if you notice Go uses
01 for the day of the month,
02 for the month
03 for hours,
04 for minutes
05 for second
and so on
Therefore for example for parsing 2020-01-29, layout string should be 06-01-02 or 2006-01-02.
You can refer to the full placeholder layout table at this link - https://golangbyexample.com/parse-time-in-golang/

Go language time.Parse() for timestamps with no timezone

In Go I'm trying to use the time.Parse() function from the time package to convert a string timestamp into a Time object. I know Go has an uncommon way of representing the time format your timestamps are in by providing it with an example of how their reference time (Mon Jan 2 15:04:05 -0700 MST 2006) would be displayed in your format. I'm still having issues with errors however. Here is an example of one of my timestamps:
Tue Nov 27 09:09:29 UTC 2012
Here is what the call I'm making looks like:
t, err := time.Parse("Mon Jan 02 22:04:05 UTC 2006", "Tue Nov 27 09:09:29 UTC 2012")
So basically what I've done here is try and match the formatting for day name/month name/day number, the hour/minute/second format, the string literal "UTC" and the year format. Note that I've increased the hours field of the Go reference format by 7 (from 15 to 22) to account for the fact that their timestamp is in a negative 7 timezone and all my timestamps are in a UTC timezone.
The error I get is:
parsing time "Tue Nov 27 09:09:29 UTC 2012" as "Mon Jan 02 22:04:05 UTC 2006": cannot parse ":09:29 UTC 2012" as "2"
What am I doing wrong here? Am I misinterpreting how to use time.Parse() or is my use case not supported for some reason?
Your format string should be:
Mon Jan 02 15:04:05 MST 2006
playground
That is, use MST for the timezone and 15 for the hour, as documented in your linked Parse function.
In this case, you can use time.UnixDate:
package main
import (
"fmt"
"time"
)
func main() {
t, e := time.Parse(time.UnixDate, "Tue Nov 27 09:09:29 UTC 2012")
if e != nil {
panic(e)
}
fmt.Println(t)
}
https://golang.org/pkg/time#UnixDate