Local DateTime support (#317)

This commit is contained in:
Thomas Pelletier
2019-10-25 14:07:46 -04:00
committed by GitHub
parent 8a362ad712
commit 3a4d7af89e
3 changed files with 185 additions and 2 deletions
+18 -2
View File
@@ -69,6 +69,7 @@ const (
var timeType = reflect.TypeOf(time.Time{}) var timeType = reflect.TypeOf(time.Time{})
var marshalerType = reflect.TypeOf(new(Marshaler)).Elem() var marshalerType = reflect.TypeOf(new(Marshaler)).Elem()
var localDateType = reflect.TypeOf(LocalDate{}) var localDateType = reflect.TypeOf(LocalDate{})
var localDateTimeType = reflect.TypeOf(LocalDateTime{})
// Check if the given marshal type maps to a Tree primitive // Check if the given marshal type maps to a Tree primitive
func isPrimitive(mtype reflect.Type) bool { func isPrimitive(mtype reflect.Type) bool {
@@ -86,7 +87,7 @@ func isPrimitive(mtype reflect.Type) bool {
case reflect.String: case reflect.String:
return true return true
case reflect.Struct: case reflect.Struct:
return mtype == timeType || mtype == localDateType || isCustomMarshaler(mtype) return mtype == timeType || mtype == localDateType || mtype == localDateTimeType || isCustomMarshaler(mtype)
default: default:
return false return false
} }
@@ -706,12 +707,27 @@ func (d *Decoder) valueFromToml(mtype reflect.Type, tval interface{}, mval1 *ref
case reflect.Bool, reflect.Struct: case reflect.Bool, reflect.Struct:
val := reflect.ValueOf(tval) val := reflect.ValueOf(tval)
if val.Type() == localDateType { switch val.Type() {
case localDateType:
localDate := val.Interface().(LocalDate) localDate := val.Interface().(LocalDate)
switch mtype { switch mtype {
case timeType: case timeType:
return reflect.ValueOf(time.Date(localDate.Year, localDate.Month, localDate.Day, 0, 0, 0, 0, time.Local)), nil return reflect.ValueOf(time.Date(localDate.Year, localDate.Month, localDate.Day, 0, 0, 0, 0, time.Local)), nil
} }
case localDateTimeType:
localDateTime := val.Interface().(LocalDateTime)
switch mtype {
case timeType:
return reflect.ValueOf(time.Date(
localDateTime.Date.Year,
localDateTime.Date.Month,
localDateTime.Date.Day,
localDateTime.Time.Hour,
localDateTime.Time.Minute,
localDateTime.Time.Second,
localDateTime.Time.Nanosecond,
time.Local)), nil
}
} }
// if this passes for when mtype is reflect.Struct, tval is a time.LocalTime // if this passes for when mtype is reflect.Struct, tval is a time.LocalTime
+165
View File
@@ -1873,3 +1873,168 @@ func TestMarshalLocalDate(t *testing.T) {
t.Errorf("expected '%s', got '%s'", expected, got) t.Errorf("expected '%s', got '%s'", expected, got)
} }
} }
func TestUnmarshalLocalDateTime(t *testing.T) {
examples := []struct {
name string
in string
out LocalDateTime
}{
{
name: "normal",
in: "1979-05-27T07:32:00",
out: LocalDateTime{
Date: LocalDate{
Year: 1979,
Month: 5,
Day: 27,
},
Time: LocalTime{
Hour: 7,
Minute: 32,
Second: 0,
Nanosecond: 0,
},
}},
{
name: "with nanoseconds",
in: "1979-05-27T00:32:00.999999",
out: LocalDateTime{
Date: LocalDate{
Year: 1979,
Month: 5,
Day: 27,
},
Time: LocalTime{
Hour: 0,
Minute: 32,
Second: 0,
Nanosecond: 999999000,
},
},
},
}
for i, example := range examples {
toml := fmt.Sprintf(`date = %s`, example.in)
t.Run(fmt.Sprintf("ToLocalDateTime_%d_%s", i, example.name), func(t *testing.T) {
type dateStruct struct {
Date LocalDateTime
}
var obj dateStruct
err := Unmarshal([]byte(toml), &obj)
if err != nil {
t.Fatal(err)
}
if obj.Date != example.out {
t.Errorf("expected '%s', got '%s'", example.out, obj.Date)
}
})
t.Run(fmt.Sprintf("ToTime_%d_%s", i, example.name), func(t *testing.T) {
type dateStruct struct {
Date time.Time
}
var obj dateStruct
err := Unmarshal([]byte(toml), &obj)
if err != nil {
t.Fatal(err)
}
if obj.Date.Year() != example.out.Date.Year {
t.Errorf("expected year %d, got %d", example.out.Date.Year, obj.Date.Year())
}
if obj.Date.Month() != example.out.Date.Month {
t.Errorf("expected month %d, got %d", example.out.Date.Month, obj.Date.Month())
}
if obj.Date.Day() != example.out.Date.Day {
t.Errorf("expected day %d, got %d", example.out.Date.Day, obj.Date.Day())
}
if obj.Date.Hour() != example.out.Time.Hour {
t.Errorf("expected day %d, got %d", example.out.Time.Hour, obj.Date.Hour())
}
if obj.Date.Minute() != example.out.Time.Minute {
t.Errorf("expected day %d, got %d", example.out.Time.Minute, obj.Date.Minute())
}
if obj.Date.Second() != example.out.Time.Second {
t.Errorf("expected day %d, got %d", example.out.Time.Second, obj.Date.Second())
}
if obj.Date.Nanosecond() != example.out.Time.Nanosecond {
t.Errorf("expected day %d, got %d", example.out.Time.Nanosecond, obj.Date.Nanosecond())
}
})
}
}
func TestMarshalLocalDateTime(t *testing.T) {
type dateStruct struct {
DateTime LocalDateTime
}
examples := []struct {
name string
in LocalDateTime
out string
}{
{
name: "normal",
out: "DateTime = 1979-05-27T07:32:00\n",
in: LocalDateTime{
Date: LocalDate{
Year: 1979,
Month: 5,
Day: 27,
},
Time: LocalTime{
Hour: 7,
Minute: 32,
Second: 0,
Nanosecond: 0,
},
}},
{
name: "with nanoseconds",
out: "DateTime = 1979-05-27T00:32:00.999999000\n",
in: LocalDateTime{
Date: LocalDate{
Year: 1979,
Month: 5,
Day: 27,
},
Time: LocalTime{
Hour: 0,
Minute: 32,
Second: 0,
Nanosecond: 999999000,
},
},
},
}
for i, example := range examples {
t.Run(fmt.Sprintf("%d_%s", i, example.name), func(t *testing.T) {
obj := dateStruct{
DateTime: example.in,
}
b, err := Marshal(obj)
if err != nil {
t.Fatalf("unexpected error: %v", err)
}
got := string(b)
if got != example.out {
t.Errorf("expected '%s', got '%s'", example.out, got)
}
})
}
}
+2
View File
@@ -138,6 +138,8 @@ func tomlValueStringRepresentation(v interface{}, indent string, arraysOneElemen
return value.Format(time.RFC3339), nil return value.Format(time.RFC3339), nil
case LocalDate: case LocalDate:
return value.String(), nil return value.String(), nil
case LocalDateTime:
return value.String(), nil
case nil: case nil:
return "", nil return "", nil
} }