From dd19d0cc77c42bc4afb2753595b578403b459326 Mon Sep 17 00:00:00 2001 From: Noah Campbell Date: Sun, 11 Aug 2013 20:34:54 -0700 Subject: [PATCH 1/4] Provide better support for various date formats. Fixes #30 as long as the date is well formatted. --- hugolib/helpers.go | 29 +++++++++++----- hugolib/page_test.go | 82 ++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 103 insertions(+), 8 deletions(-) diff --git a/hugolib/helpers.go b/hugolib/helpers.go index 6bda4e3e3..0bf0fa32a 100644 --- a/hugolib/helpers.go +++ b/hugolib/helpers.go @@ -15,6 +15,7 @@ package hugolib import ( "bytes" + "errors" "fmt" "github.com/kr/pretty" "html/template" @@ -63,18 +64,30 @@ func Error(str string, a ...interface{}) { func interfaceToStringToDate(i interface{}) time.Time { s := interfaceToString(i) - d, e := time.Parse("02 Jan 06 15:04 MST", s) - if e != nil { - d, e = time.Parse("2006-01-02", s) + if d, e := parseDateWith(s, []string{ + time.RFC3339, + time.RFC1123Z, + time.RFC1123, + time.RFC822Z, + time.RFC822, + time.ANSIC, + time.UnixDate, + time.RubyDate, + }); e == nil { + return d } - if e != nil { - d, e = time.Parse("02 Jan 06", s) + return time.Unix(0, 0) +} + +func parseDateWith(s string, dates []string) (d time.Time, e error) { + for _, dateType := range dates { + if d, e = time.Parse(dateType, s); e == nil { + return + } } - - return d - + return d, errors.New(fmt.Sprintf("Unable to parse date: %s", s)) } func interfaceToBool(i interface{}) bool { diff --git a/hugolib/page_test.go b/hugolib/page_test.go index 363ee1941..bac3a95e6 100644 --- a/hugolib/page_test.go +++ b/hugolib/page_test.go @@ -5,6 +5,7 @@ import ( "io" "strings" "testing" + "time" ) var EMPTY_PAGE = "" @@ -69,6 +70,51 @@ var SIMPLE_PAGE_JSON_COMPACT = ` Text ` +var PAGE_WITH_INVALID_DATE = `--- +date: 2010-05-02 15:29:31+08:00 +--- +Page With Invalid Date (missing the T for RFC 3339)` + +var PAGE_WITH_DATE_RFC3339 = `--- +date: 2010-05-02T15:29:31+08:00 +--- +Page With Date RFC3339` + +var PAGE_WITH_DATE_RFC1123 = `--- +date: Sun, 02 May 2010 15:29:31 PST +--- +Page With Date RFC1123` + +var PAGE_WITH_DATE_RFC1123Z = `--- +date: Sun, 02 May 2010 15:29:31 +0800 +--- +Page With Date RFC1123Z` + +var PAGE_WITH_DATE_RFC822 = `--- +date: 02 May 10 15:29 PST +--- +Page With Date RFC822` + +var PAGE_WITH_DATE_RFC822Z = `--- +date: 02 May 10 15:29 +0800 +--- +Page With Date RFC822Z` + +var PAGE_WITH_DATE_ANSIC = `--- +date: Sun May 2 15:29:31 2010 +--- +Page With Date ANSIC` + +var PAGE_WITH_DATE_UnixDate = `--- +date: Sun May 2 15:29:31 PST 2010 +--- +Page With Date UnixDate` + +var PAGE_WITH_DATE_RubyDate = `--- +date: Sun May 02 15:29:31 +0800 2010 +--- +Page With Date RubyDate` + func checkError(t *testing.T, err error, expected string) { if err == nil { t.Fatalf("err is nil") @@ -169,3 +215,39 @@ func TestDegenerateInvalidFrontMatterLeadingWhitespace(t *testing.T) { t.Fatalf("Unable to parse front matter given leading whitespace: %s", err) } } + +func TestDegenerateDateFrontMatter(t *testing.T) { + p, _ := ReadFrom(strings.NewReader(PAGE_WITH_INVALID_DATE), "page/with/invalid/date") + if p.Date != time.Unix(0, 0) { + t.Fatalf("Date should be set to computer epoch. Got: %s", p.Date) + } +} + +func TestParsingDateInFrontMatter(t *testing.T) { + + for _, test := range []struct { + buf string + dt string + }{ + {PAGE_WITH_DATE_RFC3339, "2010-05-02T15:29:31+08:00"}, + {PAGE_WITH_DATE_RFC1123, "2010-05-02T15:29:31-08:00"}, + {PAGE_WITH_DATE_RFC1123Z, "2010-05-02T15:29:31+08:00"}, + {PAGE_WITH_DATE_RFC822, "2010-05-02T15:29:00-08:00"}, + {PAGE_WITH_DATE_RFC822Z, "2010-05-02T15:29:00+08:00"}, + {PAGE_WITH_DATE_ANSIC, "2010-05-02T15:29:31Z"}, + {PAGE_WITH_DATE_UnixDate, "2010-05-02T15:29:31-08:00"}, + {PAGE_WITH_DATE_RubyDate, "2010-05-02T15:29:31+08:00"}, + } { + dt, e := time.Parse(time.RFC3339, test.dt) + if e != nil { + t.Fatalf("Unable to parse date time (RFC3339) for running the test: %s", e) + } + p, err := ReadFrom(strings.NewReader(test.buf), "page/with/date") + if err != nil { + t.Fatalf("Expected to be able to parse page.") + } + if !dt.Equal(p.Date) { + t.Errorf("Date does not equal frontmatter:\n%s\nGot: %s. Diff: %s", test.buf, p.Date, dt.Sub(p.Date)) + } + } +} From f610d45cd85b622d89e1e4112e20bb8307761bf1 Mon Sep 17 00:00:00 2001 From: Noah Campbell Date: Sun, 11 Aug 2013 21:03:04 -0700 Subject: [PATCH 2/4] Add additional details to date test cases. --- hugolib/page_test.go | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/hugolib/page_test.go b/hugolib/page_test.go index bac3a95e6..3e6db411b 100644 --- a/hugolib/page_test.go +++ b/hugolib/page_test.go @@ -247,7 +247,8 @@ func TestParsingDateInFrontMatter(t *testing.T) { t.Fatalf("Expected to be able to parse page.") } if !dt.Equal(p.Date) { - t.Errorf("Date does not equal frontmatter:\n%s\nGot: %s. Diff: %s", test.buf, p.Date, dt.Sub(p.Date)) + //if dt != p.Date { + t.Errorf("Date does not equal frontmatter:\n%s\nExpecting: %s\n Got: %s. Diff: %s\n internal: %#v\n %#v", test.buf, dt, p.Date, dt.Sub(p.Date), dt, p.Date) } } } From 0305c82513dec62e1bb2ca2701f675e1ed37687e Mon Sep 17 00:00:00 2001 From: Noah Campbell Date: Mon, 12 Aug 2013 09:41:41 -0700 Subject: [PATCH 3/4] Move timezone tests to integration test. Interacting with timezones will result in checks against the filesystem. This access, by definition, is an integration test. Creating a *integration_test.go file will signify this change. When interacting with Travis-ci.org, the ubuntu boxes plus go 1.1 do not seem to support shortcode timezones, think PST. In this case, the tests are skipped. This is not ideal, but the IRC #go-nuts channel has indicated timezone support is still lacking. We should advise users of hugo that timezone support may be an issue and report any odd behavior. The workaround is to use numeric timezones (-08:00 for PST, etc.) --- .travis.yml | 6 +- hugolib/page_test.go | 83 --------------------- hugolib/page_time_integration_test.go | 103 ++++++++++++++++++++++++++ 3 files changed, 106 insertions(+), 86 deletions(-) create mode 100644 hugolib/page_time_integration_test.go diff --git a/.travis.yml b/.travis.yml index 98334df4c..b1081131c 100644 --- a/.travis.yml +++ b/.travis.yml @@ -1,3 +1,3 @@ -language: go -go: - - 1.1 +language: go +go: + - 1.1 diff --git a/hugolib/page_test.go b/hugolib/page_test.go index 3e6db411b..363ee1941 100644 --- a/hugolib/page_test.go +++ b/hugolib/page_test.go @@ -5,7 +5,6 @@ import ( "io" "strings" "testing" - "time" ) var EMPTY_PAGE = "" @@ -70,51 +69,6 @@ var SIMPLE_PAGE_JSON_COMPACT = ` Text ` -var PAGE_WITH_INVALID_DATE = `--- -date: 2010-05-02 15:29:31+08:00 ---- -Page With Invalid Date (missing the T for RFC 3339)` - -var PAGE_WITH_DATE_RFC3339 = `--- -date: 2010-05-02T15:29:31+08:00 ---- -Page With Date RFC3339` - -var PAGE_WITH_DATE_RFC1123 = `--- -date: Sun, 02 May 2010 15:29:31 PST ---- -Page With Date RFC1123` - -var PAGE_WITH_DATE_RFC1123Z = `--- -date: Sun, 02 May 2010 15:29:31 +0800 ---- -Page With Date RFC1123Z` - -var PAGE_WITH_DATE_RFC822 = `--- -date: 02 May 10 15:29 PST ---- -Page With Date RFC822` - -var PAGE_WITH_DATE_RFC822Z = `--- -date: 02 May 10 15:29 +0800 ---- -Page With Date RFC822Z` - -var PAGE_WITH_DATE_ANSIC = `--- -date: Sun May 2 15:29:31 2010 ---- -Page With Date ANSIC` - -var PAGE_WITH_DATE_UnixDate = `--- -date: Sun May 2 15:29:31 PST 2010 ---- -Page With Date UnixDate` - -var PAGE_WITH_DATE_RubyDate = `--- -date: Sun May 02 15:29:31 +0800 2010 ---- -Page With Date RubyDate` - func checkError(t *testing.T, err error, expected string) { if err == nil { t.Fatalf("err is nil") @@ -215,40 +169,3 @@ func TestDegenerateInvalidFrontMatterLeadingWhitespace(t *testing.T) { t.Fatalf("Unable to parse front matter given leading whitespace: %s", err) } } - -func TestDegenerateDateFrontMatter(t *testing.T) { - p, _ := ReadFrom(strings.NewReader(PAGE_WITH_INVALID_DATE), "page/with/invalid/date") - if p.Date != time.Unix(0, 0) { - t.Fatalf("Date should be set to computer epoch. Got: %s", p.Date) - } -} - -func TestParsingDateInFrontMatter(t *testing.T) { - - for _, test := range []struct { - buf string - dt string - }{ - {PAGE_WITH_DATE_RFC3339, "2010-05-02T15:29:31+08:00"}, - {PAGE_WITH_DATE_RFC1123, "2010-05-02T15:29:31-08:00"}, - {PAGE_WITH_DATE_RFC1123Z, "2010-05-02T15:29:31+08:00"}, - {PAGE_WITH_DATE_RFC822, "2010-05-02T15:29:00-08:00"}, - {PAGE_WITH_DATE_RFC822Z, "2010-05-02T15:29:00+08:00"}, - {PAGE_WITH_DATE_ANSIC, "2010-05-02T15:29:31Z"}, - {PAGE_WITH_DATE_UnixDate, "2010-05-02T15:29:31-08:00"}, - {PAGE_WITH_DATE_RubyDate, "2010-05-02T15:29:31+08:00"}, - } { - dt, e := time.Parse(time.RFC3339, test.dt) - if e != nil { - t.Fatalf("Unable to parse date time (RFC3339) for running the test: %s", e) - } - p, err := ReadFrom(strings.NewReader(test.buf), "page/with/date") - if err != nil { - t.Fatalf("Expected to be able to parse page.") - } - if !dt.Equal(p.Date) { - //if dt != p.Date { - t.Errorf("Date does not equal frontmatter:\n%s\nExpecting: %s\n Got: %s. Diff: %s\n internal: %#v\n %#v", test.buf, dt, p.Date, dt.Sub(p.Date), dt, p.Date) - } - } -} diff --git a/hugolib/page_time_integration_test.go b/hugolib/page_time_integration_test.go new file mode 100644 index 000000000..20844abac --- /dev/null +++ b/hugolib/page_time_integration_test.go @@ -0,0 +1,103 @@ +package hugolib + +import ( + "fmt" + "os" + "strings" + "testing" + "time" +) + +var PAGE_WITH_INVALID_DATE = `--- +date: 2010-05-02 15:29:31+08:00 +--- +Page With Invalid Date (missing the T for RFC 3339)` + +var PAGE_WITH_DATE_RFC3339 = `--- +date: 2010-05-02T15:29:31+08:00 +--- +Page With Date RFC3339` + +var PAGE_WITH_DATE_RFC1123 = `--- +date: Sun, 02 May 2010 15:29:31 PST +--- +Page With Date RFC1123` + +var PAGE_WITH_DATE_RFC1123Z = `--- +date: Sun, 02 May 2010 15:29:31 +0800 +--- +Page With Date RFC1123Z` + +var PAGE_WITH_DATE_RFC822 = `--- +date: 02 May 10 15:29 PST +--- +Page With Date RFC822` + +var PAGE_WITH_DATE_RFC822Z = `--- +date: 02 May 10 15:29 +0800 +--- +Page With Date RFC822Z` + +var PAGE_WITH_DATE_ANSIC = `--- +date: Sun May 2 15:29:31 2010 +--- +Page With Date ANSIC` + +var PAGE_WITH_DATE_UnixDate = `--- +date: Sun May 2 15:29:31 PST 2010 +--- +Page With Date UnixDate` + +var PAGE_WITH_DATE_RubyDate = `--- +date: Sun May 02 15:29:31 +0800 2010 +--- +Page With Date RubyDate` + +func TestDegenerateDateFrontMatter(t *testing.T) { + p, _ := ReadFrom(strings.NewReader(PAGE_WITH_INVALID_DATE), "page/with/invalid/date") + if p.Date != time.Unix(0, 0) { + t.Fatalf("Date should be set to computer epoch. Got: %s", p.Date) + } +} + +func TestParsingDateInFrontMatter(t *testing.T) { + tests := []struct { + buf string + dt string + }{ + {PAGE_WITH_DATE_RFC3339, "2010-05-02T15:29:31+08:00"}, + {PAGE_WITH_DATE_RFC1123Z, "2010-05-02T15:29:31+08:00"}, + {PAGE_WITH_DATE_RFC822Z, "2010-05-02T15:29:00+08:00"}, + {PAGE_WITH_DATE_ANSIC, "2010-05-02T15:29:31Z"}, + {PAGE_WITH_DATE_RubyDate, "2010-05-02T15:29:31+08:00"}, + } + + tzShortCodeTests := []struct { + buf string + dt string + }{ + {PAGE_WITH_DATE_RFC1123, "2010-05-02T15:29:31-08:00"}, + {PAGE_WITH_DATE_RFC822, "2010-05-02T15:29:00-08:00"}, + {PAGE_WITH_DATE_UnixDate, "2010-05-02T15:29:31-08:00"}, + } + + if _, err := time.LoadLocation("PST"); err == nil { + tests = append(tests, tzShortCodeTests...) + } else { + fmt.Fprintf(os.Stderr, "Skipping shortname timezone tests.\n") + } + + for _, test := range tests { + dt, e := time.Parse(time.RFC3339, test.dt) + if e != nil { + t.Fatalf("Unable to parse date time (RFC3339) for running the test: %s", e) + } + p, err := ReadFrom(strings.NewReader(test.buf), "page/with/date") + if err != nil { + t.Fatalf("Expected to be able to parse page.") + } + if !dt.Equal(p.Date) { + t.Errorf("Date does not equal frontmatter:\n%s\nExpecting: %s\n Got: %s. Diff: %s\n internal: %#v\n %#v", test.buf, dt, p.Date, dt.Sub(p.Date), dt, p.Date) + } + } +} From 3fdcd0ba7c42cabe2b51c768bc7f36d859241eb0 Mon Sep 17 00:00:00 2001 From: Noah Campbell Date: Mon, 12 Aug 2013 14:13:31 -0700 Subject: [PATCH 4/4] Support for non-standard formats. Forgot to include existing formats. Integration tests include new format. --- hugolib/helpers.go | 4 ++++ hugolib/page_time_integration_test.go | 30 ++++++++++++++++++++++++--- 2 files changed, 31 insertions(+), 3 deletions(-) diff --git a/hugolib/helpers.go b/hugolib/helpers.go index 0bf0fa32a..b2eb39673 100644 --- a/hugolib/helpers.go +++ b/hugolib/helpers.go @@ -74,6 +74,10 @@ func interfaceToStringToDate(i interface{}) time.Time { time.ANSIC, time.UnixDate, time.RubyDate, + "2006-01-02 15:04:05Z07:00", + "02 Jan 06 15:04 MST", + "2006-01-02", + "02 Jan 2006", }); e == nil { return d } diff --git a/hugolib/page_time_integration_test.go b/hugolib/page_time_integration_test.go index 20844abac..3053bea20 100644 --- a/hugolib/page_time_integration_test.go +++ b/hugolib/page_time_integration_test.go @@ -9,15 +9,20 @@ import ( ) var PAGE_WITH_INVALID_DATE = `--- -date: 2010-05-02 15:29:31+08:00 +date: 2010-05-02_15:29:31+08:00 --- -Page With Invalid Date (missing the T for RFC 3339)` +Page With Invalid Date (replace T with _ for RFC 3339)` var PAGE_WITH_DATE_RFC3339 = `--- date: 2010-05-02T15:29:31+08:00 --- Page With Date RFC3339` +var PAGE_WITH_DATE_RFC3339_NO_T = `--- +date: 2010-05-02 15:29:31+08:00 +--- +Page With Date RFC3339_NO_T` + var PAGE_WITH_DATE_RFC1123 = `--- date: Sun, 02 May 2010 15:29:31 PST --- @@ -53,6 +58,21 @@ date: Sun May 02 15:29:31 +0800 2010 --- Page With Date RubyDate` +var PAGE_WITH_DATE_HugoYearNumeric = `--- +date: 2010-05-02 +--- +Page With Date HugoYearNumeric` + +var PAGE_WITH_DATE_HugoYear = `--- +date: 02 May 2010 +--- +Page With Date HugoYear` + +var PAGE_WITH_DATE_HugoLong = `--- +date: 02 May 2010 15:29 PST +--- +Page With Date HugoLong` + func TestDegenerateDateFrontMatter(t *testing.T) { p, _ := ReadFrom(strings.NewReader(PAGE_WITH_INVALID_DATE), "page/with/invalid/date") if p.Date != time.Unix(0, 0) { @@ -66,10 +86,13 @@ func TestParsingDateInFrontMatter(t *testing.T) { dt string }{ {PAGE_WITH_DATE_RFC3339, "2010-05-02T15:29:31+08:00"}, + {PAGE_WITH_DATE_RFC3339_NO_T, "2010-05-02T15:29:31+08:00"}, {PAGE_WITH_DATE_RFC1123Z, "2010-05-02T15:29:31+08:00"}, {PAGE_WITH_DATE_RFC822Z, "2010-05-02T15:29:00+08:00"}, {PAGE_WITH_DATE_ANSIC, "2010-05-02T15:29:31Z"}, {PAGE_WITH_DATE_RubyDate, "2010-05-02T15:29:31+08:00"}, + {PAGE_WITH_DATE_HugoYearNumeric, "2010-05-02T00:00:00Z"}, + {PAGE_WITH_DATE_HugoYear, "2010-05-02T00:00:00Z"}, } tzShortCodeTests := []struct { @@ -77,8 +100,9 @@ func TestParsingDateInFrontMatter(t *testing.T) { dt string }{ {PAGE_WITH_DATE_RFC1123, "2010-05-02T15:29:31-08:00"}, - {PAGE_WITH_DATE_RFC822, "2010-05-02T15:29:00-08:00"}, + {PAGE_WITH_DATE_RFC822, "2010-05-02T15:29:00-08:00Z"}, {PAGE_WITH_DATE_UnixDate, "2010-05-02T15:29:31-08:00"}, + {PAGE_WITH_DATE_HugoLong, "2010-05-02T15:21:00+08:00"}, } if _, err := time.LoadLocation("PST"); err == nil {