Repository: go-openapi/strfmt
Pull request #105 (2023-03-02 16:01:26)

Adds IsZero functionality to DateTime
This PR adds a new function `IsZero` to the DateTime struct. The goal of this is to satisfy a common interface, IsZeroer, used by go-yaml/yaml marshaller. `IsZero` already exists on the time.Time object, so this does not introduce any novel implementation.

For reference, here is the [documentation
note](https://pkg.go.dev/gopkg.in/yaml.v3#Marshal) on using IsZero by the `(yaml).Marshal` function. If you have a struct generated by swagger that use a proto timestamp, a strfmt.DateTime is used. When you try to marshal this to yaml, _all_ DateTime fields are skipped and excluded.

bg451: cc @casualjim
codecov[bot]: # [Codecov](https://codecov.io/gh/go-openapi/strfmt/pull/105?src=pr&el=h1&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=go-openapi) Report
> Merging [#105](https://codecov.io/gh/go-openapi/strfmt/pull/105?src=pr&el=desc&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=go-openapi) (d203a72) into [master](https://codecov.io/gh/go-openapi/strfmt/commit/a239ff1fdb33f31ad470c61555006bb09260aa59?el=desc&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=go-openapi) (a239ff1) will **increase** coverage by `0.01%`.
> The diff coverage is `100.00%`.

```diff
@@            Coverage Diff             @@
##           master     #105      +/-   ##
==========================================
+ Coverage   81.52%   81.54%   +0.01%     
==========================================
  Files          12       12              
  Lines        2019     2021       +2     
==========================================
+ Hits         1646     1648       +2     
  Misses        295      295              
  Partials       78       78              
```


| [Impacted Files](https://codecov.io/gh/go-openapi/strfmt/pull/105?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=go-openapi) | Coverage Δ | |
|---|---|---|
| [time.go](https://codecov.io/gh/go-openapi/strfmt/pull/105?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=go-openapi#diff-dGltZS5nbw==) | `90.84% <100.00%> (+0.13%)` | :arrow_up: |

Help us with your feedback. Take ten seconds to tell us [how you rate us](https://about.codecov.io/nps?utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=go-openapi). Have a feature suggestion? [Share it here.](https://app.codecov.io/gh/feedback/?utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=go-openapi)

casualjim: This could also use a companion method that considers `time.Unix(0,0)` to be zero. Go's idea of a mindate is different from other languages.

This seems to be the pattern for it: https://github.com/golang/go/issues/33597#issuecomment-548232450
bg451: > This could also use a companion method that considers time.Unix(0,0) to be zero.

@casualjim Hm so turns out this behavior isn't as simple as you'd hope. The equivalency is timezone dependent, e.g. `time.Unix(0, 0)` != `time.Unix(0, 0).UTC()` on my laptop. When the locale is UTC, the equivalency check is fine, but this seems pretty prone to cause bugs to due the nuance of it. 
casualjim: We can do something like a package level method that captures the IsZero predicate.  Similar to these vars: https://github.com/go-openapi/strfmt/blob/master/time.go#L83-L88

I think this should be configurable because if you are dealing with javascript or java clients and they send a min date, they should be able to determine what is the Zero date for their deployment

Patch:
diff --git a/time.go b/time.go
index 023676e..dbc4ffc 100644
--- a/time.go
+++ b/time.go
@@ -29,6 +29,12 @@ import (
 	"go.mongodb.org/mongo-driver/bson/bsontype"
 )
 
+var (
+	// UnixZero sets the zero unix timestamp we want to compare against.
+	// Unix 0 for an EST timezone is not equivalent to a UTC timezone.
+	UnixZero = time.Unix(0, 0).UTC()
+)
+
 func init() {
 	dt := DateTime{}
 	Default.Add("datetime", &dt, IsDateTime)
@@ -123,6 +129,16 @@ func (t DateTime) String() string {
 	return NormalizeTimeForMarshal(time.Time(t)).Format(MarshalFormat)
 }
 
+// IsZero returns whether the date time is a zero value
+func (t DateTime) IsZero() bool {
+	return time.Time(t).IsZero()
+}
+
+// IsUnixZerom returns whether the date time is equivalent to time.Unix(0, 0).UTC().
+func (t DateTime) IsUnixZero() bool {
+	return time.Time(t) == UnixZero
+}
+
 // MarshalText implements the text marshaller interface
 func (t DateTime) MarshalText() ([]byte, error) {
 	return []byte(t.String()), nil


Test patch:
diff --git a/time_test.go b/time_test.go
index c1a96c0..9c20410 100644
--- a/time_test.go
+++ b/time_test.go
@@ -53,6 +53,27 @@ func TestNewDateTime(t *testing.T) {
 	assert.EqualValues(t, time.Unix(0, 0).UTC(), NewDateTime())
 }
 
+func TestIsZero(t *testing.T) {
+	var empty DateTime
+	assert.True(t, empty.IsZero())
+	assert.False(t, DateTime(time.Unix(100, 5)).IsZero())
+
+	// time.Unix(0,0) does not produce a true zero value struct,
+	// so this is expected to fail.
+	assert.False(t, NewDateTime().IsZero())
+}
+
+func TestIsUnixZero(t *testing.T) {
+	dt := NewDateTime()
+	assert.True(t, dt.IsUnixZero())
+	assert.NotEqual(t, dt.IsZero(), dt.IsUnixZero())
+	// Test configuring UnixZero
+	estLocation := time.FixedZone("EST", int((-5 * time.Hour).Seconds()))
+	estUnixZero := time.Unix(0, 0).In(estLocation)
+	UnixZero = estUnixZero
+	assert.True(t, DateTime(estUnixZero).IsUnixZero())
+}
+
 func TestParseDateTime_errorCases(t *testing.T) {
 	_, err := ParseDateTime("yada")
 	assert.Error(t, err)