Skip to content

Commit

Permalink
Decoder: support all TOML datetime formats
Browse files Browse the repository at this point in the history
Fixes #96.
  • Loading branch information
cespare committed Apr 26, 2016
1 parent f936976 commit f0aeabc
Show file tree
Hide file tree
Showing 3 changed files with 73 additions and 33 deletions.
50 changes: 45 additions & 5 deletions decode_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -343,14 +343,54 @@ Description = "da base"
}
}

func TestDecodeBadTimestamp(t *testing.T) {
var x struct {
T time.Time
func TestDecodeDatetime(t *testing.T) {
const noTimestamp = "2006-01-02T15:04:05"
for _, tt := range []struct {
s string
t string
format string
}{
{"1979-05-27T07:32:00Z", "1979-05-27T07:32:00Z", time.RFC3339},
{"1979-05-27T00:32:00-07:00", "1979-05-27T00:32:00-07:00", time.RFC3339},
{
"1979-05-27T00:32:00.999999-07:00",
"1979-05-27T00:32:00.999999-07:00",
time.RFC3339,
},
{"1979-05-27T07:32:00", "1979-05-27T07:32:00", noTimestamp},
{
"1979-05-27T00:32:00.999999",
"1979-05-27T00:32:00.999999",
noTimestamp,
},
{"1979-05-27", "1979-05-27T00:00:00", noTimestamp},
} {
var x struct{ D time.Time }
input := "d = " + tt.s
if _, err := Decode(input, &x); err != nil {
t.Errorf("Decode(%q): got error: %s", input, err)
continue
}
want, err := time.ParseInLocation(tt.format, tt.t, time.Local)
if err != nil {
panic(err)
}
if !x.D.Equal(want) {
t.Errorf("Decode(%q): got %s; want %s", input, x.D, want)
}
}
}

func TestDecodeBadDatetime(t *testing.T) {
var x struct{ T time.Time }
for _, s := range []string{
"T = 123", "T = 2006-01-50T00:00:00Z", "T = 2006-01-30T00:00:00",
"123",
"2006-01-50T00:00:00Z",
"2006-01-30T00:00",
"2006-01-30T",
} {
if _, err := Decode(s, &x); err == nil {
input := "T = " + s
if _, err := Decode(input, &x); err == nil {
t.Errorf("Expected invalid DateTime error for %q", s)
}
}
Expand Down
37 changes: 12 additions & 25 deletions lex.go
Original file line number Diff line number Diff line change
Expand Up @@ -661,10 +661,7 @@ func lexNumberOrDate(lx *lexer) stateFn {
}
switch r {
case '-':
if lx.pos-lx.start != 5 {
return lx.errorf("All ISO8601 dates must be in full Zulu form.")
}
return lexDateAfterYear
return lexDatetime
case '_':
return lexNumber
case '.', 'e', 'E':
Expand All @@ -676,29 +673,19 @@ func lexNumberOrDate(lx *lexer) stateFn {
return lx.pop()
}

// lexDateAfterYear consumes a full Zulu Datetime in ISO8601 format.
// It assumes that "YYYY-" has already been consumed.
func lexDateAfterYear(lx *lexer) stateFn {
formats := []rune{
// digits are '0'.
// everything else is direct equality.
'0', '0', '-', '0', '0',
'T',
'0', '0', ':', '0', '0', ':', '0', '0',
'Z',
// lexDatetime consumes a Datetime, to a first approximation.
// The parser validates that it matches one of the accepted formats.
func lexDatetime(lx *lexer) stateFn {
r := lx.next()
if isDigit(r) {
return lexDatetime
}
for _, f := range formats {
r := lx.next()
if f == '0' {
if !isDigit(r) {
return lx.errorf("Expected digit in ISO8601 datetime, "+
"but found %q instead.", r)
}
} else if f != r {
return lx.errorf("Expected %q in ISO8601 datetime, "+
"but found %q instead.", f, r)
}
switch r {
case '-', 'T', ':', '.', 'Z':
return lexDatetime
}

lx.backup()
lx.emit(itemDatetime)
return lx.pop()
}
Expand Down
19 changes: 16 additions & 3 deletions parse.go
Original file line number Diff line number Diff line change
Expand Up @@ -236,9 +236,22 @@ func (p *parser) value(it item) (interface{}, tomlType) {
}
return num, p.typeOfPrimitive(it)
case itemDatetime:
t, err := time.Parse("2006-01-02T15:04:05Z", it.val)
if err != nil {
p.panicf("Invalid RFC3339 Zulu DateTime: '%s'.", it.val)
var t time.Time
var ok bool
var err error
for _, format := range []string{
"2006-01-02T15:04:05Z07:00",
"2006-01-02T15:04:05",
"2006-01-02",
} {
t, err = time.ParseInLocation(format, it.val, time.Local)
if err == nil {
ok = true
break
}
}
if !ok {
p.panicf("Invalid TOML Datetime: %q.", it.val)
}
return t, p.typeOfPrimitive(it)
case itemArray:
Expand Down

0 comments on commit f0aeabc

Please sign in to comment.