用于表示结构中的可选time.Time的惯用方法

Nem*_*den 0 time struct optional-parameters go

我读过两个可选参数?Golang传递nil作为函数的可选参数?

并且仍然想知道我的案例是否更具体.

我得到的是:

type Periodical struct {
    Interval *interval.Interval
    StartsAt time.Time
    EndsAt   time.Time
}
Run Code Online (Sandbox Code Playgroud)

表示具有开始日期并且可能有也可能没有结束日期的期刊事件(期刊事件无限期地运行).

eachYear := Periodical{
    interval.Years(1),
    StartsAt: time.Parse("2 Jan 2006", "1 Jan 1970")}
Run Code Online (Sandbox Code Playgroud)

会抛出

periodical/periodical.go:31:39: cannot use nil as type time.Time in field value
Run Code Online (Sandbox Code Playgroud)

据了解, - 我没有说明EndsAt time.Time.

但那我到底做了什么呢?

我被迫有一个特殊的旗帜可以忽略EndsAt这样吗?

type Periodical struct {
    Interval     *interval.Interval
    StartsAt     time.Time
    EndsAt       time.Time
    isIndefinite bool       // This looks ugly already
}
Run Code Online (Sandbox Code Playgroud)

然后,如果我想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}
Run Code Online (Sandbox Code Playgroud)

虽然,我可以在业务逻辑中考虑这个标志,但是EndsAt设置为相同(或任何其他)日期看起来有点沉闷.

我还在periodical包上定义了一个方法,允许有一个速记周期性事件,如下所示:

func Monthly(s, e time.Time) Periodical {
    return Periodical{StartsAt: s, EndsAt: e, Interval: interval.Months(1)}
}
Run Code Online (Sandbox Code Playgroud)

我该怎么做才能省略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}
    }
}
Run Code Online (Sandbox Code Playgroud)

虽然它有诀窍,看起来很难看,不是吗?我简洁的单行代码现在分散在几行代码中.

所以,这就是为什么我想知道,实现我想要做的事情的惯用方法是什么?

icz*_*cza 8

time.Time是一个结构.它的零值 - 尽管是一个有效的时间值 - 就像从未使用过一样.您可以利用零值时间来指示缺失或未定义的时间值.甚至有一种Time.IsZero()方法可以告诉您time.Time值是否为零值.

请注意,零值时间不是像其他一些语言中的1970年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),
}
Run Code Online (Sandbox Code Playgroud)

请注意,您不能time.Parse()在此复合文字中使用,因为它返回2个值(a time.Time和an error).要么time.Date()像上面的例子一样使用,要么先创建时间值(句柄错误),只使用时间值.

告诉我是否EndsAt指定:

if eachYear.EndsAt.IsZero() {
    fmt.Println("EndsAt is missing")
}
Run Code Online (Sandbox Code Playgroud)

如果您需要将已设置(非零)时间值归零,则可以使用time.Time{}复合文字:

eachYear.StartsAt = time.Time{}
Run Code Online (Sandbox Code Playgroud)

另请注意,当编组一个time.Time值时,即使它是零值(因为它是一个有效的时间值),即使您使用该omitempty选项也会发送它.在这些情况下,您必须使用*time.Time指针或编写自定义封送程序.有关详细信息,请参阅Golang JSON omitempty with time.Time Field.