Идиоматический способ представления необязательного времени. Время в структуре - PullRequest
0 голосов
/ 07 сентября 2018

Я прочитал оба Необязательные параметры? и 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}
    }
}

Хотя это и помогает, но выглядит уродливо, не так ли? Мой краткий однострочный текст теперь разбросан по нескольким строкам кода.

Итак, вот почему я задаюсь вопросом, каков идиоматический способ go добиться того, что я пытаюсь сделать?

1 Ответ

0 голосов
/ 07 сентября 2018

time.Time является структурой. Его нулевое значение - хотя и является допустимым значением времени - как никогда не используется. Вы можете использовать нулевое значение времени, чтобы сигнализировать о пропущенном или неопределенном значении времени. Существует даже метод Time.IsZero(), который сообщает, является ли значение time.Time нулевым значением.

Обратите внимание, что время с нулевым значением не 1 января 1970 года, как на некоторых других языках, а 1 января 1 года, как вы можете видеть на Go Playground . Это также задокументировано на time.Time:

Нулевым значением типа Time является 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 значения (time.Time и 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 .

...