Parsing time in Golang - go

I'm trying to create a Time struct based on some input called dateAdded. My code is like this:
dateAdded := "November 25, 2016"
layout := "September 9, 2016"
t, err := time.Parse(layout, dateAdded)
if err != nil {
fmt.Println(err)
} else {
fmt.Println(t)
}
And I get the following error: parsing time "November 25, 2016" as "September 9, 2016": cannot parse "November 25, 2016" as "September 9, "
I assume the Parse function cannot parse every layout, but I'm curios what's the usual way of reading dates and parse them into time objects.

The layout, if you're not using one of the pre-included constant layout that comes with the time module, must be formed from the exact timestamp Mon Jan 2 15:04:05 -0700 MST 2006. Notice that each element of it is unique, so each numeric identifier can be automatically parsed. it's basically 1 (month), 2 (day), 3 (hour), 4 (minute), 5 (second), 6 (year), 7 (time zone) etc.
It's better to use one of the pre-defined standard layouts that are included with the library:
const (
ANSIC = "Mon Jan _2 15:04:05 2006"
UnixDate = "Mon Jan _2 15:04:05 MST 2006"
RubyDate = "Mon Jan 02 15:04:05 -0700 2006"
RFC822 = "02 Jan 06 15:04 MST"
RFC822Z = "02 Jan 06 15:04 -0700" // RFC822 with numeric zone
RFC850 = "Monday, 02-Jan-06 15:04:05 MST"
RFC1123 = "Mon, 02 Jan 2006 15:04:05 MST"
RFC1123Z = "Mon, 02 Jan 2006 15:04:05 -0700" // RFC1123 with numeric zone
RFC3339 = "2006-01-02T15:04:05Z07:00"
RFC3339Nano = "2006-01-02T15:04:05.999999999Z07:00"
Kitchen = "3:04PM"
// Handy time stamps.
Stamp = "Jan _2 15:04:05"
StampMilli = "Jan _2 15:04:05.000"
StampMicro = "Jan _2 15:04:05.000000"
StampNano = "Jan _2 15:04:05.000000000"
)

Your layout date is wrong. Should be "January 2, 2006". As the specs say:
The layout defines the format by showing how the reference time,
defined to be Mon Jan 2 15:04:05 -0700 MST 2006 would be interpreted
if it were the value

You should treat it as an example which you provide to time.Provide. And it should be of concrete value described in the documentation.
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
A playground with correct variant.

Related

Define custom time format

I am trying to write a custom date format string as required by my application.
Using the Go time package I get the format using a clumsy function (see below).
Also since this function will be called millions of times every day, I want this to be super efficient too. Is there a POSIX style formatting available in Go?
package main
import (
"fmt"
"time"
)
func main() {
t := time.Now()
fmt.Printf("Time now is %d%02d%02d%02d%02d%02d",
t.Year(), t.Month(), t.Day(), t.Hour(), t.Minute(), t.Second())
}
In Go there are built in layouts to pass to the .Format() method of Time (see func (Time) Format), one of these could be what you are looking for:
func main() {
fmt.Println(time.Now().Format(time.ANSIC))
fmt.Println(time.Now().Format(time.UnixDate))
fmt.Println(time.Now().Format(time.RFC3339))
}
This is going to be faster than fmt.Printf("...", t.Year(), t.Month(), ...), but we're talking about microseconds "faster" here, so there really is no big difference.
Output:
Sun Jun 10 13:18:09 2018
Sun Jun 10 13:18:09 UTC 2018
2018-06-10T13:18:09Z
There are a lot more predefined layouts, so you can check all of them out and see if one of them fits your needs. Here they are directly from the source code:
const (
ANSIC = "Mon Jan _2 15:04:05 2006"
UnixDate = "Mon Jan _2 15:04:05 MST 2006"
RubyDate = "Mon Jan 02 15:04:05 -0700 2006"
RFC822 = "02 Jan 06 15:04 MST"
RFC822Z = "02 Jan 06 15:04 -0700" // RFC822 with numeric zone
RFC850 = "Monday, 02-Jan-06 15:04:05 MST"
RFC1123 = "Mon, 02 Jan 2006 15:04:05 MST"
RFC1123Z = "Mon, 02 Jan 2006 15:04:05 -0700" // RFC1123 with numeric zone
RFC3339 = "2006-01-02T15:04:05Z07:00"
RFC3339Nano = "2006-01-02T15:04:05.999999999Z07:00"
Kitchen = "3:04PM"
// Handy time stamps.
Stamp = "Jan _2 15:04:05"
StampMilli = "Jan _2 15:04:05.000"
StampMicro = "Jan _2 15:04:05.000000"
StampNano = "Jan _2 15:04:05.000000000"
)
Other than that, it's just a matter of creating your own layout, there really is no difference.
To create a custom layout string, quoting from the documentation:
The layout string used by the Parse function and Format method shows by example how the reference time should be represented. We stress that one must show how the reference time is formatted, not a time of the user's choosing. Thus each layout string is a representation of the time stamp: Jan 2 15:04:05 2006 MST.
So, for example:
fmt.Println(time.Now().Format("Mon January 2, 2006 - 15:04:05.000"))
gives:
Sun June 10, 2018 - 17:49:32.557
If you want something different you'll just have to format the date Jan 2 15:04:05 2006 MST how you would want it to be displayed. You can also take a look at the relative page on Go by Example.

Go parse string to time

I've got the following string:
Sun, 03 Jan 2016 10:00:07 CET
Id like to parse it into time, but cannot seem to figure out how to write the format.
This is what I've got so far:
layout := "Mon, 01 Jan 03:04:05"
t, _ := time.Parse(layout, "Sun, 03 Jan 2016 10:00:07 CET")
fmt.Println(t)
Output I get is:
0001-01-01 00:00:00 +0000 UTC
First of all: You're silently ignoring the error this is returned as second return value of time.Parse. I'd suggest handling the error appropriately, instead.
Secondly, let's have a look at the documentation of time.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 would be interpreted if it were the value; it serves as an example of the input format. The same interpretation will then be made to the input string.
The time.Parse function expects its layout parameter to represent a fixed example date. So, in order to parse the date Sun, 03 Jan 2016 10:00:07 CET, the appropriate example layout string should be Mon, 02 Jan 2006 15:04:05 MST:
layout := "Mon, 02 Jan 2006 15:04:05 MST"
t, err := time.Parse(layout, "Sun, 03 Jan 2016 10:00:07 CET")
if err != nil {
// handle the error somehow!
}
fmt.Println(t)

convert YYYYMMDD string to a valid date in Go

I want to convert a string "20101011" to a valid date (2010-10-11), but could not figure our how to do it.
I tried:
now := time.Now()
date := now.Format("20101011")
and
date, _ := time.Parse("20101011", "20101011")
neither one worked.
Package time
import "time"
Constants
const (
ANSIC = "Mon Jan _2 15:04:05 2006"
UnixDate = "Mon Jan _2 15:04:05 MST 2006"
RubyDate = "Mon Jan 02 15:04:05 -0700 2006"
RFC822 = "02 Jan 06 15:04 MST"
RFC822Z = "02 Jan 06 15:04 -0700" // RFC822 with numeric zone
RFC850 = "Monday, 02-Jan-06 15:04:05 MST"
RFC1123 = "Mon, 02 Jan 2006 15:04:05 MST"
RFC1123Z = "Mon, 02 Jan 2006 15:04:05 -0700" // RFC1123 with numeric zone
RFC3339 = "2006-01-02T15:04:05Z07:00"
RFC3339Nano = "2006-01-02T15:04:05.999999999Z07:00"
Kitchen = "3:04PM"
// Handy time stamps.
Stamp = "Jan _2 15:04:05"
StampMilli = "Jan _2 15:04:05.000"
StampMicro = "Jan _2 15:04:05.000000"
StampNano = "Jan _2 15:04:05.000000000"
)
These are predefined layouts for use in Time.Format and Time.Parse.
The reference time used in the layouts is the specific time:
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
To define your own format, write down what the reference time would
look like formatted your way; see the values of constants like ANSIC,
StampMicro or Kitchen for examples. The model is to demonstrate what
the reference time looks like so that the Format and Parse methods can
apply the same transformation to a general time value.
Use the time format string "20060102" for YYYYMMDD. Use the time format string "2006-01-02" for YYYY-MM-DD.
For example,
package main
import (
"fmt"
"time"
)
func main() {
now := time.Now()
fmt.Println(now)
date := now.Format("20060102")
fmt.Println(date)
date = now.Format("2006-01-02")
fmt.Println(date)
date2, err := time.Parse("20060102", "20101011")
if err == nil {
fmt.Println(date2)
}
}
Output:
2009-11-10 23:00:00 +0000 UTC
20091110
2009-11-10
2010-10-11 00:00:00 +0000 UTC
You can do the following:
dateStr := "20210131" // date in 'String' data type
dateValue, _ := time.Parse("20060102", dateStr) // convert 'String' to 'Time' data type
fmt.Println(dateValue) // output: 2021-01-31 00:00:00 +0000 UTC
dateStr = dateValue.Format("2006-01-02") // Format return a 'string' in your specified layout (YYYY-MM-DD)
fmt.Println(dateStr) // Output: 2021-01-31
If you don't mind another library, Google's civil package has a nice ParseDate() which skips the interim time.Time conversion. It can be used like so:
func ParseDate(s string) (Date, error) {
date, err := civil.ParseDate(s)
return Date{date}, err
}

How to parse non-standard date / time in Go ? Mitteleuropäische Sommerzeit

I'm trying to parse dates received in email headers. Recently I've got stuck on this one Thu, 7 Aug 2014 14:03:05 +0200 (Mitteleuropäische Sommerzeit). What kind of layout am I supposed to use ? Mon, 02 Jan 2006 15:04:05 -0700 (MST) doesn't do the trick.
I've also tried the workaround below but it still doesn't work. I'm not sure why Mitt... is not being replaced.
if strings.Contains(d, "Mitteleuropäische Sommerzeit") {
d = strings.Replace(d, "Mitteleuropäische Sommerzeit", "CEST", 1)
}
The Mitteleuropäische Sommerzeit part is indeed not recognized by the time package. But it works perfectly when you replace it by CEST:
var d = "Thu, 7 Aug 2014 14:03:05 +0200 (Mitteleuropäische Sommerzeit)"
_, err := time.Parse("Mon, _2 Jan 2006 15:04:05 -0700 (MST)", d)
if err != nil {
fmt.Println(err) // There is indeed an error
}
d = strings.Replace(d, "Mitteleuropäische Sommerzeit", "CEST", 1)
t, err := time.Parse("Mon, _2 Jan 2006 15:04:05 -0700 (MST)", d)
if err != nil {
fmt.Println(err) // No error this time
}
fmt.Println(t) // 2014-08-07 14:03:05 +0200 CEST
On playground.
Do not forget to write _2 instead of 2 in your layout, so that days with two figures can be parsed too.
It is redundant to try and parse the parenthesized timezone name when you already know the correct numeric offset:
Thu, 7 Aug 2014 14:03:05 +0200 (Mitteleuropäische Sommerzeit)
is equally specific as
Thu, 7 Aug 2014 14:03:05 +0200
The only additional information you get out of the first representation is that it was summer when the email was sent and that the region from which the email's author comes from is central Europe (literally "central Europe summertime").
This will not, however, affect the time.Time value's representation in contrast to the second string because the daylight saving adjustment is already reflected in the numerical timezone representation +0200 which would be +0100 during winter.
In other words, in this case you can ignore the spelled-out timezone specification.

What is the purpose of the "Handy time stamps" in the time package?

Reference:
I am trying to figure out what the "Handy Time Stamp" are used for in the time package.
I can parse dates just fine using the other constants such as RFC
t, _ := time.Parse(time.RFC822, "02 Jan 06 15:04 MST")
fmt.Println(t.Unix())
Output 1136214240
vs
t, _ := time.Parse(time.Stamp, "Jan _2 15:04:05")
fmt.Println(t.Unix())
Output: -62135596800
The last output is wrong. What am I missing here? How are these timestamps useful?
Below is the Godoc for time constants:
const (
ANSIC = "Mon Jan _2 15:04:05 2006"
UnixDate = "Mon Jan _2 15:04:05 MST 2006"
RubyDate = "Mon Jan 02 15:04:05 -0700 2006"
RFC822 = "02 Jan 06 15:04 MST"
RFC822Z = "02 Jan 06 15:04 -0700" // RFC822 with numeric zone
RFC850 = "Monday, 02-Jan-06 15:04:05 MST"
RFC1123 = "Mon, 02 Jan 2006 15:04:05 MST"
RFC1123Z = "Mon, 02 Jan 2006 15:04:05 -0700" // RFC1123 with numeric zone
RFC3339 = "2006-01-02T15:04:05Z07:00"
RFC3339Nano = "2006-01-02T15:04:05.999999999Z07:00"
Kitchen = "3:04PM"
// Handy time stamps.
Stamp = "Jan _2 15:04:05"
StampMilli = "Jan _2 15:04:05.000"
StampMicro = "Jan _2 15:04:05.000000"
StampNano = "Jan _2 15:04:05.000000000"
)
The last output is wrong. What am I missing here? How are these timestamps useful?
You are definitely missing an error check here, let's add it
t, err := time.Parse(time.Stamp, "Jan _2 15:04:05")
fmt.Println(err)
fmt.Println(t.Unix())
Output:
parsing time "Jan _2 15:04:05" as "Jan _2 15:04:05": cannot parse "_2
15:04:05" as "_2"
-62135596800
The correct string would be "Jan 2 15:04:05" (note the double space between Jan and 2). About underscore from the docs:
Within the format string, an underscore _ represents a space that may
be replaced by a digit if the following number (a day) has two digits;
for compatibility with fixed-width Unix time formats.
Then, why it's representations as UNIX time is negative, let's check:
t, err := time.Parse(time.Stamp, "Jan 2 15:04:05")
fmt.Println(err)
fmt.Println(t)
Output:
<nil>
0000-01-02 15:04:05 +0000 UTC
So it's negative because the year is 0000.
And finally, where it can be useful? For example, to measure duration of time-consuming operations. You can output to logs current time in one of Stamp formats along with some messages like "Started doing this", "Finished doing that". Then, because it's fixed-width format and without unnecessary year information - it's easy to read the logs, easy to parse such logs.
This format is actually used in "syslog" in *nix.

Resources