Commit 63d63929 authored by Rob Pike's avatar Rob Pike

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
parent ccbcefe2
...@@ -2,6 +2,7 @@ package time ...@@ -2,6 +2,7 @@ package time
import ( import (
"bytes" "bytes"
"once"
"os" "os"
"strconv" "strconv"
) )
...@@ -14,12 +15,16 @@ const ( ...@@ -14,12 +15,16 @@ const (
// These are predefined layouts for use in Time.Format. // These are predefined layouts for use in Time.Format.
// The standard time used in the layouts is: // 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. // which is Unix time 1136243045.
// (Think of it as 01/02 03:04:05PM '06 -0700.) // (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 ( const (
ANSIC = "Mon Jan 2 15:04:05 2006" ANSIC = "Mon Jan _2 15:04:05 2006"
UnixDate = "Mon Jan 2 15:04:05 MST 2006" UnixDate = "Mon Jan _2 15:04:05 MST 2006"
RFC850 = "Monday, 02-Jan-06 15:04:05 MST" RFC850 = "Monday, 02-Jan-06 15:04:05 MST"
RFC1123 = "Mon, 02 Jan 2006 15:04:05 MST" RFC1123 = "Mon, 02 Jan 2006 15:04:05 MST"
Kitchen = "3:04PM" Kitchen = "3:04PM"
...@@ -36,6 +41,7 @@ const ( ...@@ -36,6 +41,7 @@ const (
stdLongWeekDay = "Monday" stdLongWeekDay = "Monday"
stdWeekDay = "Mon" stdWeekDay = "Mon"
stdDay = "2" stdDay = "2"
stdUnderDay = "_2"
stdZeroDay = "02" stdZeroDay = "02"
stdHour = "15" stdHour = "15"
stdHour12 = "3" stdHour12 = "3"
...@@ -118,20 +124,24 @@ func charType(c uint8) int { ...@@ -118,20 +124,24 @@ func charType(c uint8) int {
switch { switch {
case '0' <= c && c <= '9': case '0' <= c && c <= '9':
return numeric return numeric
case c == '_': // underscore; treated like a number when printing
return numeric
case 'a' <= c && c < 'z', 'A' <= c && c <= 'Z': case 'a' <= c && c < 'z', 'A' <= c && c <= 'Z':
return alphabetic return alphabetic
} }
return separator return separator
} }
func zeroPad(i int) string { func pad(i int, padding string) string {
s := strconv.Itoa(i) s := strconv.Itoa(i)
if i < 10 { if i < 10 {
s = "0" + s s = padding + s
} }
return s return s
} }
func zeroPad(i int) string { return pad(i, "0") }
// Format returns a textual representation of the time value formatted // Format returns a textual representation of the time value formatted
// according to layout. The layout defines the format by showing the // according to layout. The layout defines the format by showing the
// representation of a standard time, which is then used to describe // representation of a standard time, which is then used to describe
...@@ -168,6 +178,8 @@ func (t *Time) Format(layout string) string { ...@@ -168,6 +178,8 @@ func (t *Time) Format(layout string) string {
p = longDayNames[t.Weekday] p = longDayNames[t.Weekday]
case stdDay: case stdDay:
p = strconv.Itoa(t.Day) p = strconv.Itoa(t.Day)
case stdUnderDay:
p = pad(t.Day, " ")
case stdZeroDay: case stdZeroDay:
p = zeroPad(t.Day) p = zeroPad(t.Day)
case stdHour: case stdHour:
...@@ -250,6 +262,21 @@ func (e *ParseError) String() string { ...@@ -250,6 +262,21 @@ func (e *ParseError) String() string {
strconv.Quote(e.Value) + e.Message 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. // Parse parses a formatted string and returns the time value it represents.
// The layout defines the format by showing the representation of a standard // 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 // 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) { ...@@ -296,15 +323,21 @@ func Parse(alayout, avalue string) (*Time, os.Error) {
} }
p := value[0:i] p := value[0:i]
value = value[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 pieceType == separator {
if len(p) != len(reference) { if len(p) != len(reference) {
// must be exactly a following minus sign // must be exactly a following minus sign
if len(p) != len(reference)+1 || p[len(p)-1] != '-' { pp := collapseSpaces(p)
return nil, &ParseError{Layout: alayout, Value: avalue, Message: formatErr + alayout} 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 var err os.Error
...@@ -335,7 +368,7 @@ func Parse(alayout, avalue string) (*Time, os.Error) { ...@@ -335,7 +368,7 @@ func Parse(alayout, avalue string) (*Time, os.Error) {
t.Weekday, err = lookup(shortDayNames, p) t.Weekday, err = lookup(shortDayNames, p)
case stdLongWeekDay: case stdLongWeekDay:
t.Weekday, err = lookup(longDayNames, p) t.Weekday, err = lookup(longDayNames, p)
case stdDay, stdZeroDay: case stdDay, stdUnderDay, stdZeroDay:
t.Day, err = strconv.Atoi(p) t.Day, err = strconv.Atoi(p)
if t.Day < 0 || 31 < t.Day { if t.Day < 0 || 31 < t.Day {
// TODO: be more thorough in date check? // TODO: be more thorough in date check?
...@@ -422,6 +455,7 @@ func Parse(alayout, avalue string) (*Time, os.Error) { ...@@ -422,6 +455,7 @@ func Parse(alayout, avalue string) (*Time, os.Error) {
// It's a valid format. // It's a valid format.
t.Zone = p t.Zone = p
// Can we find it in the table? // Can we find it in the table?
once.Do(setupZone)
for _, z := range zones { for _, z := range zones {
if p == z.zone.name { if p == z.zone.name {
t.ZoneOffset = z.zone.utcoff t.ZoneOffset = z.zone.utcoff
......
...@@ -165,6 +165,9 @@ var parseTests = []ParseTest{ ...@@ -165,6 +165,9 @@ var parseTests = []ParseTest{
ParseTest{"RFC850", RFC850, "Thursday, 04-Feb-10 21:00:57 PST", true, true}, 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{"RFC1123", RFC1123, "Thu, 04 Feb 2010 21:00:57 PST", true, true},
ParseTest{"ISO8601", ISO8601, "2010-02-04T21:00:57-0800", true, false}, 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) { func TestParse(t *testing.T) {
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment