表示可选时间的惯用方式。结构中的时间

时间:2018-09-07 06:55:39

标签: go time struct optional-parameters

我已经读过Optional Parameters?Golang pass nil as optional argument to a function?

仍然想知道我的案情是否更具体。

我所拥有的是:

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}
    }
}

尽管它能解决问题,但看起来很难看,不是吗?我简洁的单行代码现在分散在几行代码中。

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

1 个答案:

答案 0 :(得分:8)

time.Time是一个结构。尽管zero value是有效的时间值,但从未使用过。您可以利用零值时间来表示缺少或未定义的时间值。甚至还有一种Time.IsZero()方法可以告诉您time.Time的值是否为零。

请注意,零值时间不同于其他语言,它不是1970年1月1日,而是1年1月1日,如您在Go Playground上看到的那样。在time.Time中也有记录:

  

时间类型的零值为1年1月1日,00:00:00.000000000 UTC。由于实际上不太可能出现此时间,因此IsZero方法提供了一种检测未明确初始化的时间的简单方法。

此外,在创建Periodical值时,请使用键控composite literal,您可以省略不需要设置的字段,从而将其保留为零值:

eachYear := Periodical{
    Interval: interval.Years(1),
    StartsAt: time.Date(1970, 1, 1, 0, 0, 0, 0, time.UTC),
}

请注意,您不能在此复合文字中使用time.Parse(),因为它会返回2个值(time.Timeerror)。如上例中那样使用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