diff --git a/model/time.go b/model/time.go index 77b82b2b..871e78c7 100644 --- a/model/time.go +++ b/model/time.go @@ -14,6 +14,7 @@ package model import ( + "encoding/json" "fmt" "math" "regexp" @@ -254,6 +255,22 @@ func (d Duration) String() string { return r } +// MarshalJSON implements the json.Marshaler interface. +func (d Duration) MarshalJSON() ([]byte, error) { + return json.Marshal(d.String()) +} + +// UnmarshalJSON implements the json.Unmarshaler interface. +func (d *Duration) UnmarshalJSON(bytes []byte) error { + var err error + var s string + if err := json.Unmarshal(bytes, &s); err != nil { + return err + } + *d, err = ParseDuration(s) + return err +} + // MarshalText implements the encoding.TextMarshaler interface. func (d *Duration) MarshalText() ([]byte, error) { return []byte(d.String()), nil diff --git a/model/time_test.go b/model/time_test.go index 14fb9eb9..a65b859d 100644 --- a/model/time_test.go +++ b/model/time_test.go @@ -14,6 +14,7 @@ package model import ( + "encoding/json" "strconv" "testing" "time" @@ -230,6 +231,82 @@ func TestDuration_UnmarshalText(t *testing.T) { } } +func TestDuration_UnmarshalJSON(t *testing.T) { + var cases = []struct { + in string + out time.Duration + + expectedString string + }{ + { + in: `"0"`, + out: 0, + expectedString: `"0s"`, + }, { + in: `"0w"`, + out: 0, + expectedString: `"0s"`, + }, { + in: `"0s"`, + out: 0, + }, { + in: `"324ms"`, + out: 324 * time.Millisecond, + }, { + in: `"3s"`, + out: 3 * time.Second, + }, { + in: `"5m"`, + out: 5 * time.Minute, + }, { + in: `"1h"`, + out: time.Hour, + }, { + in: `"4d"`, + out: 4 * 24 * time.Hour, + }, { + in: `"4d1h"`, + out: 4*24*time.Hour + time.Hour, + }, { + in: `"14d"`, + out: 14 * 24 * time.Hour, + expectedString: `"2w"`, + }, { + in: `"3w"`, + out: 3 * 7 * 24 * time.Hour, + }, { + in: `"3w2d1h"`, + out: 3*7*24*time.Hour + 2*24*time.Hour + time.Hour, + expectedString: `"23d1h"`, + }, { + in: `"10y"`, + out: 10 * 365 * 24 * time.Hour, + }, + } + + for _, c := range cases { + var d Duration + err := json.Unmarshal([]byte(c.in), &d) + if err != nil { + t.Errorf("Unexpected error on input %q", c.in) + } + if time.Duration(d) != c.out { + t.Errorf("Expected %v but got %v", c.out, d) + } + expectedString := c.expectedString + if c.expectedString == "" { + expectedString = c.in + } + bytes, err := json.Marshal(d) + if err != nil { + t.Errorf("Unexpected error on marshal of %v: %s", d, err) + } + if string(bytes) != expectedString { + t.Errorf("Expected duration string %q but got %q", c.in, d.String()) + } + } +} + func TestParseBadDuration(t *testing.T) { var cases = []string{ "1",