Como fazer comparação de data / hora

98

Existe alguma opção de comparação de datas no Go? Tenho que classificar os dados com base na data e hora - independentemente. Portanto, posso permitir um objeto que ocorre dentro de um intervalo de datas, desde que também ocorra dentro de um intervalo de tempos. Neste modelo, eu não poderia simplesmente selecionar a data mais antiga, a hora mais recente / a data mais recente, a hora mais recente e os segundos Unix () compará-los. Eu realmente aprecio qualquer sugestão.

Por fim, escrevi um módulo de comparação de string de análise de tempo para verificar se um tempo está dentro de um intervalo. No entanto, isso não está indo muito bem; Eu tenho alguns problemas escancarados. Vou postar isso aqui apenas por diversão, mas espero que haja uma maneira melhor de comparar os tempos.

package main

import (
    "strconv"
    "strings"
)

func tryIndex(arr []string, index int, def string) string {
    if index <= len(arr)-1 {
        return arr[index]
    }
    return def
}

/*
 * Takes two strings of format "hh:mm:ss" and compares them.
 * Takes a function to compare individual sections (split by ":").
 * Note: strings can actually be formatted like "h", "hh", "hh:m",
 * "hh:mm", etc. Any missing parts will be added lazily.
 */
func timeCompare(a, b string, compare func(int, int) (bool, bool)) bool {
    aArr := strings.Split(a, ":")
    bArr := strings.Split(b, ":")
    // Catches margins.
    if (b == a) {
        return true
    }
    for i := range aArr {
        aI, _ := strconv.Atoi(tryIndex(aArr, i, "00"))
        bI, _ := strconv.Atoi(tryIndex(bArr, i, "00"))
        res, flag := compare(aI, bI)
        if res {
            return true
        } else if flag { // Needed to catch case where a > b and a is the lower limit
            return false
        }
    }
    return false
}

func timeGreaterEqual(a, b int) (bool, bool) {return a > b, a < b}
func timeLesserEqual(a, b int) (bool, bool) {return a < b, a > b}

/*
 * Returns true for two strings formmated "hh:mm:ss".
 * Note: strings can actually be formatted like "h", "hh", "hh:m",
 * "hh:mm", etc. Any missing parts will be added lazily.
 */
func withinTime(timeRange, time string) bool {
    rArr := strings.Split(timeRange, "-")
    if timeCompare(rArr[0], rArr[1], timeLesserEqual) {
        afterStart := timeCompare(rArr[0], time, timeLesserEqual)
        beforeEnd := timeCompare(rArr[1], time, timeGreaterEqual)
        return afterStart && beforeEnd
    }
    // Catch things like `timeRange := "22:00:00-04:59:59"` which will happen
    // with UTC conversions from local time.
    // THIS IS THE BROKEN PART I BELIEVE
    afterStart := timeCompare(rArr[0], time, timeLesserEqual)
    beforeEnd := timeCompare(rArr[1], time, timeGreaterEqual)
    return afterStart || beforeEnd
}

Então, TLDR, escrevi uma função withinTimeRange (range, time), mas não está funcionando totalmente corretamente. (Na verdade, principalmente apenas o segundo caso, em que um intervalo de tempo cruza os dias é quebrado. A parte original funcionou, acabei de perceber que precisaria levar isso em consideração ao fazer conversões para UTC do local.)

Se houver uma maneira melhor (de preferência embutida), adoraria saber mais sobre ela!

NOTA: Apenas como exemplo, resolvi esse problema em Javascript com esta função:

function withinTime(start, end, time) {
    var s = Date.parse("01/01/2011 "+start);
    var e = Date.parse("01/0"+(end=="24:00:00"?"2":"1")+"/2011 "+(end=="24:00:00"?"00:00:00":end));
    var t = Date.parse("01/01/2011 "+time);
    return s <= t && e >= t;
}

No entanto, eu realmente quero fazer esse filtro do lado do servidor.

Eatonphil
fonte

Respostas:

110

Use o pacote de tempo para trabalhar com informações de tempo no Go.

Os instantes de tempo podem ser comparados usando os métodos Before, After e Equal. O método Sub subtrai dois instantes, produzindo uma Duração. O método Add adiciona um Time e um Duration, produzindo um Time.

Exemplo de jogo :

package main

import (
    "fmt"
    "time"
)

func inTimeSpan(start, end, check time.Time) bool {
    return check.After(start) && check.Before(end)
}

func main() {
    start, _ := time.Parse(time.RFC822, "01 Jan 15 10:00 UTC")
    end, _ := time.Parse(time.RFC822, "01 Jan 16 10:00 UTC")

    in, _ := time.Parse(time.RFC822, "01 Jan 15 20:00 UTC")
    out, _ := time.Parse(time.RFC822, "01 Jan 17 10:00 UTC")

    if inTimeSpan(start, end, in) {
        fmt.Println(in, "is between", start, "and", end, ".")
    }

    if !inTimeSpan(start, end, out) {
        fmt.Println(out, "is not between", start, "and", end, ".")
    }
}
andybalholm
fonte
Talvez eu não saiba ler, mas não vi nada ali sobre comparações de tempo. Se estiver lá, você poderia me indicar um artigo exato?
eatonphil
12
Experimente godoc.org/time#Time.Equal ou godoc.org/time#Time.After para uma comparação simples ou godoc.org/time#Time.Sub para descobrir a diferença entre duas vezes.
andybalholm
1
"relata se o instante de tempo t é após u." misterioso
Damien Roche
22

Para comparação entre duas vezes, use time.Sub ()

// utc life
loc, _ := time.LoadLocation("UTC")

// setup a start and end time
createdAt := time.Now().In(loc).Add(1 * time.Hour)
expiresAt := time.Now().In(loc).Add(4 * time.Hour)

// get the diff
diff := expiresAt.Sub(createdAt)
fmt.Printf("Lifespan is %+v", diff)

O programa produz:

Lifespan is 3h0m0s

http://play.golang.org/p/bbxeTtd4L6

Charney Kaye
fonte
Esta é a melhor resposta.
MithunS
15

Para o caso de quando o seu intervalo terminar é uma data sem horas como "de 01-01-2017 a todo o dia de 2016-01-16", é melhor ajustar o intervalo para 23 horas, 59 minutos e 59 segundos, como:

end = end.Add(time.Duration(23*time.Hour) + time.Duration(59*time.Minute) + time.Duration(59*time.Second)) 

if now.After(start) && now.Before(end) {
    ...
}
Oleg Neumyvakin
fonte
1
Exatamente o que eu precisava para comparar um TimeStamp armazenado com a hora atual.
PGP_Protector
1

Protocolos recentes preferem o uso de RFC3339 por documentação de pacote de tempo golang .

Em geral, RFC1123Z deve ser usado em vez de RFC1123 para servidores que insistem nesse formato, e RFC3339 deve ser usado para novos protocolos. RFC822, RFC822Z, RFC1123 e RFC1123Z são úteis para formatação; quando usados ​​com time.Parse, eles não aceitam todos os formatos de hora permitidos pelas RFCs.

cutOffTime, _ := time.Parse(time.RFC3339, "2017-08-30T13:35:00Z")
// POSTDATE is a date time field in DB (datastore)
query := datastore.NewQuery("db").Filter("POSTDATE >=", cutOffTime).
Deepakssn
fonte
-1

O seguinte resolveu meu problema de converter string em data

pacote principal

import (
    "fmt"
    "time"
)

func main() {
    value  := "Thu, 05/19/11, 10:47PM"
    // Writing down the way the standard time would look like formatted our way
    layout := "Mon, 01/02/06, 03:04PM"
    t, _ := time.Parse(layout, value)
    fmt.Println(t)
}

// => "Thu May 19 22:47:00 +0000 2011"

Obrigado a paul adam smith

Suryakrupa
fonte
1
Isso tudo é bom mas não tem muito a ver com pergunta, deos isso?
matthias krull
Você está certo @matthiaskrull. Não responde à questão de comparar datas, mas ajuda parcialmente na análise de datas com facilidade.
suryakrupa de
Então faça isso e alguns outros. Só estou dizendo que vincular algo nos comentários seria uma opção melhor do que responder com bits úteis aleatórios.
matthias krull de