diff --git a/civil/civil.go b/civil/civil.go index 29272ef26a3..a39246802af 100644 --- a/civil/civil.go +++ b/civil/civil.go @@ -107,6 +107,11 @@ func (d1 Date) After(d2 Date) bool { return d2.Before(d1) } +// IsZero reports whether date fields are set to their default value. +func (d Date) IsZero() bool { + return (d.Year == 0) && (int(d.Month) == 0) && (d.Day == 0) +} + // MarshalText implements the encoding.TextMarshaler interface. // The output is the result of d.String(). func (d Date) MarshalText() ([]byte, error) { @@ -175,6 +180,11 @@ func (t Time) IsValid() bool { return TimeOf(tm) == t } +// IsZero reports whether time fields are set to their default value. +func (t Time) IsZero() bool { + return (t.Hour == 0) && (t.Minute == 0) && (t.Second == 0) && (t.Nanosecond == 0) +} + // MarshalText implements the encoding.TextMarshaler interface. // The output is the result of t.String(). func (t Time) MarshalText() ([]byte, error) { @@ -262,6 +272,11 @@ func (dt1 DateTime) After(dt2 DateTime) bool { return dt2.Before(dt1) } +// IsZero reports whether datetime fields are set to their default value. +func (dt DateTime) IsZero() bool { + return dt.Date.IsZero() && dt.Time.IsZero() +} + // MarshalText implements the encoding.TextMarshaler interface. // The output is the result of dt.String(). func (dt DateTime) MarshalText() ([]byte, error) { diff --git a/civil/civil_test.go b/civil/civil_test.go index 0720092e215..18711de2832 100644 --- a/civil/civil_test.go +++ b/civil/civil_test.go @@ -193,6 +193,24 @@ func TestDateAfter(t *testing.T) { } } +func TestDateIsZero(t *testing.T) { + for _, test := range []struct { + date Date + want bool + }{ + {Date{2000, 2, 29}, false}, + {Date{10000, 12, 31}, false}, + {Date{-1, 0, 0}, false}, + {Date{0, 0, 0}, true}, + {Date{}, true}, + } { + got := test.date.IsZero() + if got != test.want { + t.Errorf("%#v: got %t, want %t", test.date, got, test.want) + } + } +} + func TestTimeToString(t *testing.T) { for _, test := range []struct { str string @@ -260,6 +278,24 @@ func TestTimeIsValid(t *testing.T) { } } +func TestTimeIsZero(t *testing.T) { + for _, test := range []struct { + time Time + want bool + }{ + {Time{0, 0, 0, 0}, true}, + {Time{}, true}, + {Time{0, 0, 0, 1}, false}, + {Time{-1, 0, 0, 0}, false}, + {Time{0, -1, 0, 0}, false}, + } { + got := test.time.IsZero() + if got != test.want { + t.Errorf("%#v: got %t, want %t", test.time, got, test.want) + } + } +} + func TestDateTimeToString(t *testing.T) { for _, test := range []struct { str string @@ -383,6 +419,28 @@ func TestDateTimeAfter(t *testing.T) { } } +func TestDateTimeIsZero(t *testing.T) { + for _, test := range []struct { + dt DateTime + want bool + }{ + {DateTime{Date{2016, 3, 20}, Time{0, 0, 0, 0}}, false}, + {DateTime{Date{}, Time{5, 44, 20, 0}}, false}, + {DateTime{Date{2016, 3, 20}, Time{}}, false}, + {DateTime{Date{0, 0, 0}, Time{5, 16, 47, 2}}, false}, + {DateTime{Date{2021, 9, 5}, Time{9, 30, 51, 6}}, false}, + {DateTime{Date{}, Time{}}, true}, + {DateTime{Date{0, 0, 0}, Time{0, 0, 0, 0}}, true}, + {DateTime{Date{}, Time{0, 0, 0, 0}}, true}, + {DateTime{Date{0, 0, 0}, Time{}}, true}, + } { + got := test.dt.IsZero() + if got != test.want { + t.Errorf("%#v: got %t, want %t", test.dt, got, test.want) + } + } +} + func TestMarshalJSON(t *testing.T) { for _, test := range []struct { value interface{}