Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
13 changes: 11 additions & 2 deletions xtime/time.go
Original file line number Diff line number Diff line change
Expand Up @@ -82,10 +82,19 @@ func (d *Duration) UnmarshalYAML(value *yaml.Node) error {

// UnmarshalJSON implements json.Unmarshaler
func (d *Duration) UnmarshalJSON(bs []byte) error {
if len(bs) <= 2 {
// A null leaves the value alone, as it does for a type that has no
// Unmarshaler, and as UnmarshalYAML already does.
if string(bs) == "null" {
return nil
}
dur, err := ParseDuration(string(bs[1 : len(bs)-1]))
var s string
if err := json.Unmarshal(bs, &s); err != nil {
return err
}
if s == "" {
return nil
}
dur, err := ParseDuration(s)
if err != nil {
return err
}
Expand Down
27 changes: 27 additions & 0 deletions xtime/time_unmarshal_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -160,3 +160,30 @@ durationPointer: 168h0m1s
t.Errorf("JSON round-trip mismatch: got %+v", back)
}
}

func TestDuration_UnmarshalJSONNull(t *testing.T) {
type s struct {
Dur Duration `json:"dur" yaml:"dur"`
}

// A JSON null leaves the field alone, as it does for a type with no
// Unmarshaler, and as the YAML side of this type already does.
v := s{Dur: Duration(time.Hour)}
if err := json.Unmarshal([]byte(`{"dur":null}`), &v); err != nil {
t.Fatalf("json null: %v", err)
}
if v.Dur.D() != time.Hour {
t.Errorf("json null overwrote the field: got %v, want 1h0m0s", v.Dur.D())
}
var y s
if err := yaml.Unmarshal([]byte("dur: null\n"), &y); err != nil {
t.Fatalf("yaml null: %v", err)
}

// A JSON number is not a duration. Reading it as zero hides the
// mistake from whoever wrote the config.
var n s
if err := json.Unmarshal([]byte(`{"dur":12}`), &n); err == nil {
t.Errorf("expected an error for a JSON number, got %v", n.Dur.D())
}
}