From 63d639295ccf5ebfdcf9ac0802b5b9fbf8502380 Mon Sep 17 00:00:00 2001 From: Rob Pike Date: Wed, 13 Jan 2010 14:39:30 +1100 Subject: [PATCH] don't worry about the number of spaces when parsing. allow an underscore to stand for a space or digit if the following number is >=10. R=rsc CC=golang-dev https://golang.org/cl/186115 --- src/pkg/time/format.go | 56 +++++++++++++++++++++++++++++++-------- src/pkg/time/time_test.go | 3 +++ 2 files changed, 48 insertions(+), 11 deletions(-) diff --git a/src/pkg/time/format.go b/src/pkg/time/format.go index 8f81df980c..745d9fb153 100644 --- a/src/pkg/time/format.go +++ b/src/pkg/time/format.go @@ -2,6 +2,7 @@ package time import ( "bytes" + "once" "os" "strconv" ) @@ -14,12 +15,16 @@ const ( // These are predefined layouts for use in Time.Format. // The standard time used in the layouts is: -// Mon Jan 2 15:04:05 MST 2006 (MST is GMT-0700) +// Mon Jan 2 15:04:05 MST 2006 (MST is GMT-0700) // which is Unix time 1136243045. // (Think of it as 01/02 03:04:05PM '06 -0700.) +// 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. const ( - ANSIC = "Mon Jan 2 15:04:05 2006" - UnixDate = "Mon Jan 2 15:04:05 MST 2006" + ANSIC = "Mon Jan _2 15:04:05 2006" + UnixDate = "Mon Jan _2 15:04:05 MST 2006" RFC850 = "Monday, 02-Jan-06 15:04:05 MST" RFC1123 = "Mon, 02 Jan 2006 15:04:05 MST" Kitchen = "3:04PM" @@ -36,6 +41,7 @@ const ( stdLongWeekDay = "Monday" stdWeekDay = "Mon" stdDay = "2" + stdUnderDay = "_2" stdZeroDay = "02" stdHour = "15" stdHour12 = "3" @@ -118,20 +124,24 @@ func charType(c uint8) int { switch { case '0' <= c && c <= '9': return numeric + case c == '_': // underscore; treated like a number when printing + return numeric case 'a' <= c && c < 'z', 'A' <= c && c <= 'Z': return alphabetic } return separator } -func zeroPad(i int) string { +func pad(i int, padding string) string { s := strconv.Itoa(i) if i < 10 { - s = "0" + s + s = padding + s } return s } +func zeroPad(i int) string { return pad(i, "0") } + // Format returns a textual representation of the time value formatted // according to layout. The layout defines the format by showing the // representation of a standard time, which is then used to describe @@ -168,6 +178,8 @@ func (t *Time) Format(layout string) string { p = longDayNames[t.Weekday] case stdDay: p = strconv.Itoa(t.Day) + case stdUnderDay: + p = pad(t.Day, " ") case stdZeroDay: p = zeroPad(t.Day) case stdHour: @@ -250,6 +262,21 @@ func (e *ParseError) String() string { strconv.Quote(e.Value) + e.Message } +// To simplify comparison, collapse an initial run of spaces into a single space. +func collapseSpaces(s string) string { + if len(s) <= 1 || s[0] != ' ' { + return s + } + var i int + for i = 1; i < len(s); i++ { + if s[i] != ' ' { + return s[i-1:] + } + } + return " " +} + + // Parse parses a formatted string and returns the time value it represents. // The layout defines the format by showing the representation of a standard // time, which is then used to describe the string to be parsed. Predefined @@ -296,15 +323,21 @@ func Parse(alayout, avalue string) (*Time, os.Error) { } p := value[0:i] value = value[i:] - // Separators must match except possibly for a following minus sign (for negative years) + // Separators must match but: + // - initial run of spaces is treated as a single space + // - there could be a following minus sign for negative years if pieceType == separator { if len(p) != len(reference) { // must be exactly a following minus sign - if len(p) != len(reference)+1 || p[len(p)-1] != '-' { - return nil, &ParseError{Layout: alayout, Value: avalue, Message: formatErr + alayout} + pp := collapseSpaces(p) + rr := collapseSpaces(reference) + if pp != rr { + if len(pp) != len(rr)+1 || p[len(pp)-1] != '-' { + return nil, &ParseError{Layout: alayout, Value: avalue, Message: formatErr + alayout} + } + nextIsYear = true + continue } - nextIsYear = true - continue } } var err os.Error @@ -335,7 +368,7 @@ func Parse(alayout, avalue string) (*Time, os.Error) { t.Weekday, err = lookup(shortDayNames, p) case stdLongWeekDay: t.Weekday, err = lookup(longDayNames, p) - case stdDay, stdZeroDay: + case stdDay, stdUnderDay, stdZeroDay: t.Day, err = strconv.Atoi(p) if t.Day < 0 || 31 < t.Day { // TODO: be more thorough in date check? @@ -422,6 +455,7 @@ func Parse(alayout, avalue string) (*Time, os.Error) { // It's a valid format. t.Zone = p // Can we find it in the table? + once.Do(setupZone) for _, z := range zones { if p == z.zone.name { t.ZoneOffset = z.zone.utcoff diff --git a/src/pkg/time/time_test.go b/src/pkg/time/time_test.go index dab6d2073a..5036ceb13e 100644 --- a/src/pkg/time/time_test.go +++ b/src/pkg/time/time_test.go @@ -165,6 +165,9 @@ var parseTests = []ParseTest{ ParseTest{"RFC850", RFC850, "Thursday, 04-Feb-10 21:00:57 PST", true, true}, ParseTest{"RFC1123", RFC1123, "Thu, 04 Feb 2010 21:00:57 PST", true, true}, ParseTest{"ISO8601", ISO8601, "2010-02-04T21:00:57-0800", true, false}, + // Amount of white space should not matter. + ParseTest{"ANSIC", ANSIC, "Thu Feb 4 21:00:57 2010", false, true}, + ParseTest{"ANSIC", ANSIC, "Thu Feb 4 21:00:57 2010", false, true}, } func TestParse(t *testing.T) { -- 2.50.0