So führen Sie einen Datums- / Zeitvergleich durch

97

Gibt es Optionen für den Datumsvergleich in Go? Ich muss Daten nach Datum und Uhrzeit sortieren - unabhängig. Ich kann also ein Objekt zulassen, das innerhalb eines Datumsbereichs auftritt, solange es auch innerhalb eines Zeitbereichs auftritt. In diesem Modell konnte ich nicht einfach das älteste Datum, die jüngste Zeit / das späteste Datum, die späteste Zeit und Unix () Sekunden auswählen, um sie zu vergleichen. Ich würde mich über Vorschläge sehr freuen.

Letztendlich habe ich ein Zeitparsing-String-Vergleichsmodul geschrieben, um zu überprüfen, ob eine Zeit innerhalb eines Bereichs liegt. Dies ist jedoch nicht gut; Ich habe einige klaffende Probleme. Ich werde das hier nur zum Spaß posten, aber ich hoffe, es gibt einen besseren Weg, um die Zeit zu vergleichen.

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
}

Also TLDR, ich habe eine Funktion innerhalb von TimeRange (Bereich, Zeit) geschrieben, aber sie funktioniert nicht ganz richtig. (Tatsächlich ist meistens nur der zweite Fall, in dem ein Zeitbereich mehrere Tage überschreitet, unterbrochen. Der ursprüngliche Teil hat funktioniert. Ich habe nur festgestellt, dass ich dies berücksichtigen muss, wenn ich von lokal nach UTC konvertiere.)

Wenn es einen besseren (vorzugsweise eingebauten) Weg gibt, würde ich gerne davon hören!

HINWEIS: Nur als Beispiel habe ich dieses Problem in Javascript mit dieser Funktion gelöst:

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

Allerdings möchte ich diesen Filter wirklich serverseitig machen.

Eatonphil
quelle

Antworten:

108

Nutzen Sie die Zeit Paket zur Arbeit mit Zeitinformationen in Go.

Zeitpunkte können mit den Methoden Vorher, Nachher und Gleich verglichen werden. Die Sub-Methode subtrahiert zwei Zeitpunkte und erzeugt eine Dauer. Die Add-Methode fügt eine Zeit und eine Dauer hinzu und erzeugt eine Zeit.

Spielbeispiel :

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
quelle
Vielleicht kann ich nicht lesen, aber ich habe dort nichts über Zeitvergleiche gesehen. Wenn es da ist, könnten Sie mich auf einen genauen Artikel verweisen?
Eatonphil
12
Versuchen Sie godoc.org/time#Time.Equal oder godoc.org/time#Time.After für einen einfachen Vergleich oder godoc.org/time#Time.Sub , um den Unterschied zwischen zwei Zeiten herauszufinden.
Andybalholm
1
"meldet, ob der Zeitpunkt t nach u liegt." unheimlich
Damien Roche
22

Verwenden Sie zum Vergleich zwischen zwei Zeiten 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)

Das Programm gibt aus:

Lifespan is 3h0m0s

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

Charney Kaye
quelle
Dies ist die beste Antwort.
MithunS
15

Für den Fall, dass das Intervall endet, ist es ein Datum ohne Stunden wie "vom 01.01.2017 bis zum ganzen Tag vom 16.01.2017". Es ist besser, die Intervalle auf 23 Stunden, 59 Minuten und 59 Sekunden wie folgt einzustellen.

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
quelle
1
Genau das, was ich brauchte, um einen gespeicherten Zeitstempel mit der aktuellen Zeit zu vergleichen.
PGP_Protector
1

Neuere Protokolle bevorzugen die Verwendung von RFC3339 pro Golang-Zeitpaketdokumentation .

Im Allgemeinen sollte RFC1123Z anstelle von RFC1123 für Server verwendet werden, die auf diesem Format bestehen, und RFC3339 sollte für neue Protokolle bevorzugt werden. RFC822, RFC822Z, RFC1123 und RFC1123Z sind nützlich für die Formatierung. Bei Verwendung mit time.Parse akzeptieren sie nicht alle von den RFCs zugelassenen Zeitformate.

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

Das Folgende löste mein Problem beim Konvertieren von Zeichenfolgen in Datumsangaben

Paket main

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"

Vielen Dank an Paul Adam Smith

suryakrupa
quelle
1
Das ist alles schön, hat aber nicht viel mit Fragen zu tun, deos es?
Matias Krull
Sie haben Recht @matthiaskrull. Es beantwortet nicht die Frage des Datumsvergleichs, hilft aber teilweise beim einfachen Parsen von Datumsangaben.
Suryakrupa
Also mach das und ein paar andere. Ich sage nur, dass das Verknüpfen von etwas in den Kommentaren besser passt als das Antworten mit zufälligen nützlichen Bits.
Matthias Krull