go - 在结构中表示可选 time.Time 的惯用方式
问题描述
我已经阅读了两个可选参数?和Golang 将 nil 作为可选参数传递给函数?
并且仍然想知道我的情况是否更具体。
我所拥有的是:
type Periodical struct {
Interval *interval.Interval
StartsAt time.Time
EndsAt time.Time
}
表示具有开始日期并且可能有也可能没有结束日期的定期事件(定期事件运行的时间不确定)。
eachYear := Periodical{
interval.Years(1),
StartsAt: time.Parse("2 Jan 2006", "1 Jan 1970")}
会扔
periodical/periodical.go:31:39: cannot use nil as type time.Time in field value
这是可以理解的, - 我没有指定EndsAt time.Time
。
但那我真的在那里做什么呢?
我是否被迫有一个特殊的标志来忽视EndsAt
这样的事情?
type Periodical struct {
Interval *interval.Interval
StartsAt time.Time
EndsAt time.Time
isIndefinite bool // This looks ugly already
}
然后如果我想要Yearly
/Anually
我会做类似的事情
eachYear := Periodical{
interval.Years(1),
time.Parse("2 Jan 2006", "1 Jan 1970"),
time.Parse("2 Jan 2006", "1 Jan 1970"),
isIndefinite: true}
虽然,我可以在业务逻辑中解释这个标志,但是这个EndsAt
设置为相同(或任何其他)日期看起来有点乏味。
我还在periodical
包上定义了一个方法,它允许有一个速记的定期事件,如下所示:
func Monthly(s, e time.Time) Periodical {
return Periodical{StartsAt: s, EndsAt: e, Interval: interval.Months(1)}
}
我该怎么做才能省略end
(第二个参数)?我是否被迫为此使用单独的方法,或者做一些看起来有点时髦且缺乏可读性的事情:
func Monthly(s time.Time, end ...time.Time) Periodical {
if len(end) == 1 {
return Periodical{
StartsAt: s,
EndsAt: end[0],
Interval: interval.Months(1),
isIndefinite: false}
} else if len(end) > 1 {
panic("Multiple end dates are not allowed, don't know what to do with those")
} else {
return Periodical{
StartsAt: s,
EndsAt: time.Now(),
Interval: interval.Months(1),
isIndefinite: true}
}
}
虽然它成功了,但它看起来很丑,不是吗?我简洁的单行代码现在分散在几行代码中。
所以,这就是为什么我想知道,实现我想要做的事情的惯用方式是什么?
解决方案
time.Time
是一个结构。它的零值——尽管是一个有效的时间值——就像从未使用过。您可以利用零值时间来表示丢失或未定义的时间值。甚至还有一种Time.IsZero()
方法可以告诉您一个time.Time
值是否为零值。
请注意,零值时间不是像其他一些语言那样的 1970 年 1 月 1 日,而是 1 年 1 月 1 日,正如您在Go Playground上看到的那样。这也记录在time.Time
:
Time 类型的零值是 1 月 1 日,第 1 年,00:00:00.000000000 UTC。由于这个时间在实践中不太可能出现,因此 IsZero 方法提供了一种简单的方法来检测尚未明确初始化的时间。
此外,在创建Periodical
值时,使用键控复合文字,您可以省略不想设置的字段,从而将它们保持为零值:
eachYear := Periodical{
Interval: interval.Years(1),
StartsAt: time.Date(1970, 1, 1, 0, 0, 0, 0, time.UTC),
}
请注意,您不能time.Parse()
在此复合文字中使用它,因为它返回 2 个值(atime.Time
和 an error
)。要么time.Date()
像上面的例子那样使用,要么先创建时间值(处理错误),然后只使用时间值。
判断是否EndsAt
指定:
if eachYear.EndsAt.IsZero() {
fmt.Println("EndsAt is missing")
}
如果您需要将已经设置的(非零)时间值归零,您可以使用time.Time{}
复合文字:
eachYear.StartsAt = time.Time{}
另请注意,尽管在编组一个time.Time
值时,即使它是零值(因为它是一个有效的时间值),即使您使用该omitempty
选项,它也会被发送。在这些情况下,您必须使用*time.Time
指针或编写自定义封送器。详情见Golang JSON omitempty With time.Time Field。
推荐阅读
- docker - Dockerfiles 共享相同的指令,但从不同的图像构建
- c++ - 错误无法转换 'coll'(类型 'std::__cxx11::list
') 输入'const char&' - python - 管理站点不工作并抛出关于其中一个视图的错误,但视图仍在工作 django
- drake - 德雷克——有接触时模拟中的 Coredump
- javascript - 为什么 Formik.setStatus 不更新我的表单中的状态状态?
- javascript - 为什么 input#name 会出现在输出中?
- excel - How can I get the row data of the selected table header?
- spring - @Autowired 在春季批量自定义编写器中不起作用
- java - 如何在java中将字符串转换为日期“yyyy-MM-dd'T'HH:mm:ss.SSS'Z'”
- python - 在 django 中发送密码重置电子邮件时遇到困难