Cómo hacer una comparación de fecha / hora

99

¿Hay opciones para comparar fechas en Go? Tengo que ordenar los datos en función de la fecha y la hora, de forma independiente. Entonces, podría permitir un objeto que ocurra dentro de un rango de fechas siempre que también ocurra dentro de un rango de tiempos. En este modelo, no podría simplemente seleccionar la fecha más antigua, la hora más joven / la fecha más reciente, la hora más reciente y los segundos de Unix () compararlos. Realmente agradecería cualquier sugerencia.

En última instancia, escribí un módulo de comparación de cadenas de análisis de tiempo para verificar si un tiempo está dentro de un rango. Sin embargo, esto no está yendo muy bien; Tengo algunos problemas enormes. Publicaré eso aquí solo por diversión, pero espero que haya una mejor manera de comparar el tiempo.

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
}

Entonces TLDR, escribí una función withinTimeRange (rango, tiempo) pero no funciona del todo correctamente. (De hecho, en su mayoría, solo el segundo caso, donde un rango de tiempo cruza los días, se rompe. La parte original funcionó, me acabo de dar cuenta de que tendría que tener en cuenta eso al hacer conversiones a UTC desde local).

Si hay una forma mejor (preferiblemente incorporada), ¡me encantaría escucharla!

NOTA: Solo como ejemplo, resolví este problema en Javascript con esta función:

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

Sin embargo, realmente quiero hacer este filtro del lado del servidor.

Eatonphil
fuente

Respuestas:

111

Utilice el paquete de tiempo para trabajar con información de tiempo en Go.

Los instantes de tiempo se pueden comparar con los métodos Antes, Después e Igual. El método Sub resta dos instantes, produciendo una Duración. El método Add agrega un Tiempo y una Duración, produciendo un Tiempo.

Ejemplo de juego :

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
fuente
1
Tal vez no pueda leer, pero no vi nada allí sobre comparaciones de tiempo. Si está allí, ¿podría indicarme un artículo exacto?
Eatonphil
12
Pruebe godoc.org/time#Time.Equal o godoc.org/time#Time.After para una comparación simple, o godoc.org/time#Time.Sub para averiguar la diferencia entre dos tiempos.
andybalholm
1
"informa si el instante de tiempo t es posterior a u". misterioso
Damien Roche
22

Para la comparación entre dos tiempos, 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)

Los resultados del programa:

Lifespan is 3h0m0s

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

Charney Kaye
fuente
Esta es la mejor respuesta.
MithunS
15

En el caso de que el intervalo finalice, sea una fecha sin horas como "desde el 01-01-2017 hasta el día completo del 16-01-2017", es mejor ajustar el intervalo a 23 horas 59 minutos y 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
fuente
1
Exactamente lo que necesitaba para comparar una marca de tiempo almacenada con la hora actual.
PGP_Protector
1

Los protocolos recientes prefieren el uso de RFC3339 según la documentación del paquete de tiempo de golang .

En general, se debe utilizar RFC1123Z en lugar de RFC1123 para servidores que insisten en ese formato, y se debe preferir RFC3339 para nuevos protocolos. RFC822, RFC822Z, RFC1123 y RFC1123Z son útiles para formatear; cuando se usan con time.Parse no aceptan todos los formatos de hora permitidos por las RFC.

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
fuente
-1

Lo siguiente resolvió mi problema de convertir una cadena en una fecha

paquete 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"

Gracias a paul adam smith

suryakrupa
fuente
1
Eso es todo bonito pero no tiene mucho que ver con la pregunta, ¿deos?
matthias krull
Tienes razón @matthiaskrull. No responde a la pregunta de comparar fechas, pero ayuda parcialmente a analizar las fechas con facilidad.
suryakrupa
Entonces haz esto y un par de otros. Solo digo que vincular algo en los comentarios encajaría mejor que responder con bits útiles aleatorios.
matthias krull