Skip to content

Commit

Permalink
Treat datetime as a primitive when encoding.
Browse files Browse the repository at this point in the history
This CL fixes encoding of time.Time elements, which was broken in the
following three ways:

  - datetime zone was not coerced to UTC.

  - datetime elements were not printed together with the rest of simple
    types (because isStructOrMap returned true for time.Time), and were not
    separated by a newline.

  - arrays of datetime elements resulted in a panic (because isTOMLTableType
    did not handle them as a primitive type).
  • Loading branch information
dato authored and BurntSushi committed May 14, 2014
1 parent 615e355 commit eaa9a50
Show file tree
Hide file tree
Showing 2 changed files with 44 additions and 7 deletions.
28 changes: 21 additions & 7 deletions encode.go
Original file line number Diff line number Diff line change
Expand Up @@ -127,10 +127,12 @@ func (enc *Encoder) eElement(rv reflect.Value) error {
return fstr
}

// Extra special case. Time needs to be in ISO8601 format.
switch s := rv.Interface().(type) {
case time.Time:
return ws(s.UTC().Format("2006-01-02T03:04:05Z"))
// Special case time.Time as a primitive. Has to come before TextMarshaler
// below because time.Time implements encoding.TextMarshaler, but we need to
// always use UTC.
if t, ok := rv.Interface().(time.Time); ok {
t = t.In(time.FixedZone("UTC", 0))
return ws(t.Format("2006-01-02T15:04:05Z"))
}

// Special case. Use text marshaler if it's available for this value.
Expand Down Expand Up @@ -278,8 +280,11 @@ func isStructOrMap(rv reflect.Value) bool {
switch rv.Kind() {
case reflect.Interface, reflect.Ptr:
return isStructOrMap(rv.Elem())
case reflect.Map, reflect.Struct:
case reflect.Map:
return true
case reflect.Struct:
_, isDate := rv.Interface().(time.Time)
return !isDate
default:
return false
}
Expand Down Expand Up @@ -480,8 +485,14 @@ func tomlTypeName(rv reflect.Value) (typeName string, valueIsNil bool) {
return tomlTypeName(rv.Elem())
case reflect.String:
return "string", false
case reflect.Map, reflect.Struct:
case reflect.Map:
return "table", false
case reflect.Struct:
if _, isDate := rv.Interface().(time.Time); isDate {
return "datetime", false
} else {
return "table", false
}
default:
panic("unexpected reflect.Kind: " + k.String())
}
Expand Down Expand Up @@ -525,8 +536,11 @@ func isTOMLTableType(rt reflect.Type, rv reflect.Value) (bool, error) {
return isTOMLTableType(rv.Type(), rv)
}
return false, nil
case reflect.Map, reflect.Struct:
case reflect.Map:
return true, nil
case reflect.Struct:
_, isDate := rv.Interface().(time.Time)
return !isDate, nil
default:
panic("unexpected reflect.Kind: " + k.String())
}
Expand Down
23 changes: 23 additions & 0 deletions encode_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,9 @@ package toml

import (
"bytes"
"fmt"
"testing"
"time"
)

// XXX(burntsushi)
Expand All @@ -13,6 +15,9 @@ func TestEncode(t *testing.T) {
Int int `toml:"_int"`
}

date := time.Date(2014, 5, 11, 20, 30, 40, 0, time.FixedZone("IST", 3600))
dateStr := "2014-05-11T19:30:40Z"

tests := map[string]struct {
input interface{}
wantOutput string
Expand Down Expand Up @@ -64,6 +69,18 @@ func TestEncode(t *testing.T) {
}{"foo", 0},
wantOutput: `String = "foo"`,
},
"datetime field in UTC": {
input: struct{ Date time.Time }{date},
wantOutput: fmt.Sprintf("Date = %s", dateStr),
},
"datetime field as primitive": {
// Using a map here to fail if isStructOrMap() returns true for time.Time.
input: map[string]interface{}{
"Date": date,
"Int": 1,
},
wantOutput: fmt.Sprintf("Date = %s\nInt = 1", dateStr),
},
"array fields": {
input: struct {
IntArray0 [0]int
Expand All @@ -77,6 +94,12 @@ func TestEncode(t *testing.T) {
},
wantOutput: "IntSlice0 = []\nIntSlice3 = [1, 2, 3]",
},
"datetime slices": {
input: struct{ DatetimeSlice []time.Time }{
[]time.Time{date, date},
},
wantOutput: fmt.Sprintf("DatetimeSlice = [%s, %s]", dateStr, dateStr),
},
"nested arrays and slices": {
input: struct {
SliceOfArrays [][2]int
Expand Down

0 comments on commit eaa9a50

Please sign in to comment.