Skip to content

Commit

Permalink
Merge pull request #94 from mailgun/cclark/dev
Browse files Browse the repository at this point in the history
PIP-1491: support parsing month before day
  • Loading branch information
rneillj authored Nov 5, 2021
2 parents 693378c + b124215 commit 244735e
Show file tree
Hide file tree
Showing 2 changed files with 74 additions and 176 deletions.
242 changes: 68 additions & 174 deletions clock/rfc822.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,67 @@ package clock

import (
"strconv"
"time"
)

var datetimeLayouts = [48]string{
// Day first month 2nd abbreviated.
"Mon, 2 Jan 2006 15:04:05 MST",
"Mon, 2 Jan 2006 15:04:05 -0700",
"Mon, 2 Jan 2006 15:04:05 -0700 (MST)",
"2 Jan 2006 15:04:05 MST",
"2 Jan 2006 15:04:05 -0700",
"2 Jan 2006 15:04:05 -0700 (MST)",
"Mon, 2 Jan 2006 15:04 MST",
"Mon, 2 Jan 2006 15:04 -0700",
"Mon, 2 Jan 2006 15:04 -0700 (MST)",
"2 Jan 2006 15:04 MST",
"2 Jan 2006 15:04 -0700",
"2 Jan 2006 15:04 -0700 (MST)",

// Month first day 2nd abbreviated.
"Mon, Jan 2 2006 15:04:05 MST",
"Mon, Jan 2 2006 15:04:05 -0700",
"Mon, Jan 2 2006 15:04:05 -0700 (MST)",
"Jan 2 2006 15:04:05 MST",
"Jan 2 2006 15:04:05 -0700",
"Jan 2 2006 15:04:05 -0700 (MST)",
"Mon, Jan 2 2006 15:04 MST",
"Mon, Jan 2 2006 15:04 -0700",
"Mon, Jan 2 2006 15:04 -0700 (MST)",
"Jan 2 2006 15:04 MST",
"Jan 2 2006 15:04 -0700",
"Jan 2 2006 15:04 -0700 (MST)",

// Day first month 2nd not abbreviated.
"Mon, 2 January 2006 15:04:05 MST",
"Mon, 2 January 2006 15:04:05 -0700",
"Mon, 2 January 2006 15:04:05 -0700 (MST)",
"2 January 2006 15:04:05 MST",
"2 January 2006 15:04:05 -0700",
"2 January 2006 15:04:05 -0700 (MST)",
"Mon, 2 January 2006 15:04 MST",
"Mon, 2 January 2006 15:04 -0700",
"Mon, 2 January 2006 15:04 -0700 (MST)",
"2 January 2006 15:04 MST",
"2 January 2006 15:04 -0700",
"2 January 2006 15:04 -0700 (MST)",

// Month first day 2nd not abbreviated.
"Mon, January 2 2006 15:04:05 MST",
"Mon, January 2 2006 15:04:05 -0700",
"Mon, January 2 2006 15:04:05 -0700 (MST)",
"January 2 2006 15:04:05 MST",
"January 2 2006 15:04:05 -0700",
"January 2 2006 15:04:05 -0700 (MST)",
"Mon, January 2 2006 15:04 MST",
"Mon, January 2 2006 15:04 -0700",
"Mon, January 2 2006 15:04 -0700 (MST)",
"January 2 2006 15:04 MST",
"January 2 2006 15:04 -0700",
"January 2 2006 15:04 -0700 (MST)",
}

// Allows seamless JSON encoding/decoding of rfc822 formatted timestamps.
// https://www.ietf.org/rfc/rfc822.txt section 5.
type RFC822Time struct {
Expand All @@ -18,180 +77,15 @@ func NewRFC822Time(t Time) RFC822Time {

// ParseRFC822Time parses an RFC822 time string.
func ParseRFC822Time(s string) (Time, error) {
t, err := parseRFC822TimeAbreviated(s)
if err == nil {
return t, err
}
if _, ok := err.(*ParseError); !ok {
return Time{}, err
}
if t, err = parseRFC822Time(s); err == nil {
return t, err
}
if _, ok := err.(*ParseError); !ok {
return Time{}, err
}
if t, err = parseRFC822TimeAbreviatedNoSeconds(s); err == nil {
return t, err
}
if _, ok := err.(*ParseError); !ok {
return Time{}, err
}
return parseRFC822TimeNoSeconds(s)
}

// parseRFC822TimeAbbreviated attempts to parse a an RFC822 time with the month abbreviated.
func parseRFC822TimeAbreviated(s string) (Time, error) {
t, err := Parse("Mon, 2 Jan 2006 15:04:05 MST", s)
if err == nil {
return t, nil
}
if parseErr, ok := err.(*ParseError); !ok || (parseErr.LayoutElem != "MST" && parseErr.LayoutElem != "Mon") {
return Time{}, parseErr
}
if t, err = Parse("Mon, 2 Jan 2006 15:04:05 -0700", s); err == nil {
return t, nil
}
if parseErr, ok := err.(*ParseError); !ok || (parseErr.LayoutElem != "" && parseErr.LayoutElem != "Mon") {
return Time{}, parseErr
}
if t, err = Parse("Mon, 2 Jan 2006 15:04:05 -0700 (MST)", s); err == nil {
return t, err
}
if parseErr, ok := err.(*ParseError); !ok || parseErr.LayoutElem != "Mon" {
return Time{}, err
}
if t, err = Parse("2 Jan 2006 15:04:05 MST", s); err == nil {
return t, err
}
if parseErr, ok := err.(*ParseError); !ok || parseErr.LayoutElem != "MST" {
return Time{}, parseErr
}
if t, err = Parse("2 Jan 2006 15:04:05 -0700", s); err == nil {
return t, err
}
if parseErr, ok := err.(*ParseError); !ok || parseErr.LayoutElem != "" {
return Time{}, parseErr
}
if t, err = Parse("2 Jan 2006 15:04:05 -0700 (MST)", s); err == nil {
return t, err
}
return Time{}, err
}

func parseRFC822TimeAbreviatedNoSeconds(s string) (Time, error) {
t, err := Parse("Mon, 2 Jan 2006 15:04 MST", s)
if err == nil {
return t, nil
}
if parseErr, ok := err.(*ParseError); !ok || (parseErr.LayoutElem != "MST" && parseErr.LayoutElem != "Mon") {
return Time{}, parseErr
}
if t, err = Parse("Mon, 2 Jan 2006 15:04 -0700", s); err == nil {
return t, nil
}
if parseErr, ok := err.(*ParseError); !ok || (parseErr.LayoutElem != "" && parseErr.LayoutElem != "Mon") {
return Time{}, parseErr
}
if t, err = Parse("Mon, 2 Jan 2006 15:04 -0700 (MST)", s); err == nil {
return t, err
}
if parseErr, ok := err.(*ParseError); !ok || parseErr.LayoutElem != "Mon" {
return Time{}, err
}
if t, err = Parse("2 Jan 2006 15:04 MST", s); err == nil {
return t, err
}
if parseErr, ok := err.(*ParseError); !ok || parseErr.LayoutElem != "MST" {
return Time{}, parseErr
}
if t, err = Parse("2 Jan 2006 15:04 -0700", s); err == nil {
return t, err
}
if parseErr, ok := err.(*ParseError); !ok || parseErr.LayoutElem != "" {
return Time{}, parseErr
}
if t, err = Parse("2 Jan 2006 15:04 -0700 (MST)", s); err == nil {
return t, err
}
return Time{}, err
}

// parseRFC822Time attempts to parse an RFC822 time with the full month name.
func parseRFC822Time(s string) (Time, error) {
t, err := Parse("Mon, 2 January 2006 15:04:05 MST", s)
if err == nil {
return t, nil
}
if parseErr, ok := err.(*ParseError); !ok || (parseErr.LayoutElem != "MST" && parseErr.LayoutElem != "Mon") {
return Time{}, parseErr
}
if t, err = Parse("Mon, 2 January 2006 15:04:05 -0700", s); err == nil {
return t, nil
}
if parseErr, ok := err.(*ParseError); !ok || (parseErr.LayoutElem != "" && parseErr.LayoutElem != "Mon") {
return Time{}, parseErr
}
if t, err = Parse("Mon, 2 January 2006 15:04:05 -0700 (MST)", s); err == nil {
return t, err
}
if parseErr, ok := err.(*ParseError); !ok || parseErr.LayoutElem != "Mon" {
return Time{}, err
}
if t, err = Parse("2 January 2006 15:04:05 MST", s); err == nil {
return t, err
}
if parseErr, ok := err.(*ParseError); !ok || parseErr.LayoutElem != "MST" {
return Time{}, parseErr
}
if t, err = Parse("2 January 2006 15:04:05 -0700", s); err == nil {
return t, err
}
if parseErr, ok := err.(*ParseError); !ok || parseErr.LayoutElem != "" {
return Time{}, parseErr
}
if t, err = Parse("2 January 2006 15:04:05 -0700 (MST)", s); err == nil {
return t, err
}
return Time{}, err
}

func parseRFC822TimeNoSeconds(s string) (Time, error) {
t, err := Parse("Mon, 2 January 2006 15:04 MST", s)
if err == nil {
return t, nil
}
if parseErr, ok := err.(*ParseError); !ok || (parseErr.LayoutElem != "MST" && parseErr.LayoutElem != "Mon") {
return Time{}, parseErr
}
if t, err = Parse("Mon, 2 January 2006 15:04 -0700", s); err == nil {
return t, nil
}
if parseErr, ok := err.(*ParseError); !ok || (parseErr.LayoutElem != "" && parseErr.LayoutElem != "Mon") {
return Time{}, parseErr
}
if t, err = Parse("Mon, 2 January 2006 15:04 -0700 (MST)", s); err == nil {
return t, err
}
if parseErr, ok := err.(*ParseError); !ok || parseErr.LayoutElem != "Mon" {
return Time{}, err
}
if t, err = Parse("2 January 2006 15:04 MST", s); err == nil {
return t, err
}
if parseErr, ok := err.(*ParseError); !ok || parseErr.LayoutElem != "MST" {
return Time{}, parseErr
}
if t, err = Parse("2 January 2006 15:04 -0700", s); err == nil {
return t, err
}
if parseErr, ok := err.(*ParseError); !ok || parseErr.LayoutElem != "" {
return Time{}, parseErr
}
if t, err = Parse("2 January 2006 15:04 -0700 (MST)", s); err == nil {
return t, err
}
return Time{}, err
var t time.Time
var err error
for _, layout := range datetimeLayouts {
t, err = Parse(layout, s)
if err == nil {
return t, err
}
}
return t, err
}

// NewRFC822Time creates RFC822Time from a Unix timestamp (seconds from Epoch).
Expand Down
8 changes: 6 additions & 2 deletions clock/rfc822_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -137,10 +137,10 @@ func TestRFC822UnmarshalingError(t *testing.T) {
outError string
}{{
inEncoded: `{"ts": "Thu, 29 Aug 2019 11:20:07"}`,
outError: `parsing time "Thu, 29 Aug 2019 11:20:07" as "Mon, 2 January 2006 15:04 MST": cannot parse "Aug 2019 11:20:07" as "January"`,
outError: `parsing time "Thu, 29 Aug 2019 11:20:07" as "January 2 2006 15:04 -0700 (MST)": cannot parse "Thu, 29 Aug 2019 11:20:07" as "January"`,
}, {
inEncoded: `{"ts": "foo"}`,
outError: `parsing time "foo" as "2 January 2006 15:04 MST": cannot parse "foo" as "2"`,
outError: `parsing time "foo" as "January 2 2006 15:04 -0700 (MST)": cannot parse "foo" as "January"`,
}, {
inEncoded: `{"ts": 42}`,
outError: "invalid syntax",
Expand Down Expand Up @@ -170,6 +170,8 @@ func TestParseRFC822Time(t *testing.T) {
{"2 June 2021 17:06:41 GMT"},
{"2 June 2021 17:06:41 -0700"},
{"2 June 2021 17:06:41 -0700 (MST)"},
{"Wed, Nov 03 2021 17:48:06 CST"},
{"Wed, November 03 2021 17:48:06 CST"},

// Timestamps without seconds.
{"Sun, 31 Oct 2021 12:10 -5000"},
Expand All @@ -186,6 +188,8 @@ func TestParseRFC822Time(t *testing.T) {
{"2 June 2021 17:06 GMT"},
{"2 June 2021 17:06 -0700"},
{"2 June 2021 17:06 -0700 (MST)"},
{"Wed, Nov 03 2021 17:48 CST"},
{"Wed, November 03 2021 17:48 CST"},
} {
t.Run(tt.rfc822Time, func(t *testing.T) {
_, err := ParseRFC822Time(tt.rfc822Time)
Expand Down

0 comments on commit 244735e

Please sign in to comment.