Comment faire une comparaison date / heure

98

Existe-t-il des options pour comparer les dates dans Go? Je dois trier les données en fonction de la date et de l'heure - indépendamment. Je pourrais donc autoriser un objet qui se produit dans une plage de dates tant qu'il se produit également dans une plage de temps. Dans ce modèle, je ne pouvais pas simplement sélectionner la date la plus ancienne, l'heure la plus récente / la dernière date, la dernière heure et les secondes Unix () pour les comparer. J'apprécierais vraiment toutes les suggestions.

En fin de compte, j'ai écrit un module de comparaison de chaînes d'analyse temporelle pour vérifier si une heure se situe dans une plage. Cependant, cela ne va pas très bien; J'ai des problèmes béants. Je posterai ça ici juste pour le plaisir, mais j'espère qu'il existe une meilleure façon de comparer le temps.

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
}

Donc TLDR, j'ai écrit une fonction withinTimeRange (plage, heure) mais cela ne fonctionne pas totalement correctement. (En fait, la plupart du temps, le deuxième cas, où une plage de temps traverse plusieurs jours est interrompu. La partie originale a fonctionné, je viens de réaliser que je devrais en tenir compte lors de la conversion en UTC à partir du local.)

S'il y a un meilleur moyen (de préférence intégré), j'aimerais en entendre parler!

REMARQUE: à titre d'exemple, j'ai résolu ce problème en Javascript avec cette fonction:

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

Cependant, je veux vraiment faire ce filtre côté serveur.

mangeronphil
la source

Réponses:

110

Utilisez le package de temps pour travailler avec les informations de temps dans Go.

Les instants de temps peuvent être comparés à l'aide des méthodes Avant, Après et Égal. La méthode Sub soustrait deux instants, produisant une durée. La méthode Add ajoute une heure et une durée, produisant une heure.

Exemple de lecture :

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
la source
1
Peut-être que je ne peux pas lire, mais je n'ai rien vu là-dedans sur les comparaisons de temps. Si c'est là, pourriez-vous m'indiquer un article exact?
eatonphil
12
Essayez godoc.org/time#Time.Equal ou godoc.org/time#Time.After pour une simple comparaison, ou godoc.org/time#Time.Sub pour découvrir la différence entre deux heures.
andybalholm
1
"indique si l'instant t est après u." étrange
Damien Roche
22

Pour la comparaison entre deux fois, utilisez 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)

Les sorties du programme:

Lifespan is 3h0m0s

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

Charney Kaye
la source
C'est la meilleure réponse.
MithunS
15

Pour le cas où votre intervalle se termine, c'est une date sans heures comme "du 01/01/2017 à la journée du 16/01/2017", il est préférable d'ajuster l'intervalle à 23 heures 59 minutes et 59 secondes comme:

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
la source
1
Exactement ce dont j'avais besoin pour comparer un horodatage stocké avec l'heure actuelle.
PGP_Protector
1

Les protocoles récents préfèrent l'utilisation de la RFC3339 par documentation de paquet de temps de Golang .

En général, RFC1123Z doit être utilisé à la place de RFC1123 pour les serveurs qui insistent sur ce format, et RFC3339 doit être préféré pour les nouveaux protocoles. RFC822, RFC822Z, RFC1123 et RFC1123Z sont utiles pour le formatage; lorsqu'ils sont utilisés avec time.Parse ils n'acceptent pas tous les formats d'heure autorisés par les 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
la source
-1

Ce qui suit a résolu mon problème de conversion de chaîne en date

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

Merci à Paul Adam Smith

suryakrupa
la source
1
C'est bien beau mais ça n'a pas grand chose à voir avec la question, déos-il?
matthias krull
Vous avez raison @matthiaskrull. Il ne répond pas à la question de la comparaison des dates, mais aide partiellement à analyser les dates avec facilité.
suryakrupa
Alors faites ceci et quelques autres. Je dis simplement que lier quelque chose dans les commentaires conviendrait mieux que de répondre avec des bits utiles aléatoires.
matthias krull