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

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.

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.

Parsing time in Golang

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.

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)

golang RFC2822 conversion

Is there a library or function in the included libraries that converts an RFC timestamp to Unix time (or another format that I can then format into Unix time?)
For example, I'd like to change this Tue Sep 16 21:58:58 +0000 2014 to a Unix timestamp.
For example,
package main
import (
"fmt"
"time"
)
func main() {
s := "Tue Sep 16 21:58:58 +0000 2014"
const rfc2822 = "Mon Jan 02 15:04:05 -0700 2006"
t, err := time.Parse(rfc2822, s)
if err != nil {
fmt.Println(err)
return
}
u := t.Unix()
fmt.Println(u)
f := t.Format(time.UnixDate)
fmt.Println(f)
}
Output:
1410904738
Tue Sep 16 21:58:58 +0000 2014
References:
Package time
RFC2822: 3.3. Date and Time Specification
NOTE:
There is a package time format constant named RubyDate; it's a misnomer.
The Go authors were misled by Go Issue 518 which claimed that Ruby Time.now outputs Tue Jan 12 02:52:59 -0800 2010. However,
#!/usr/bin/env ruby
print Time.now
print "\n"
Output:
2014-09-20 19:40:32 -0400
Later, the issue was revised to say that Tue Jan 12 02:52:59 -0800 2010 was the date format used by the Twitter API. In the beginning, in the "Fail Whale" days, Twitter used Ruby-on-Rails, which may be why they assumed it was a Ruby date format.
I didn't use the time.RubyDate constant in my example since it's misleading. A constant named rfc2822 provides better documentation.
References:
Go: Issue 518: time.Parse - numeric time zones and spacing
Diff of format.go revision 0f80c5e80c0e
Twitter Search is Now 3x Faster

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