it-swarm.com.de

Subtrahieren Sie die Zeit mit Datum und Bash

Alle anderen Fragen im SE-Netzwerk befassen sich mit Szenarien, in denen entweder angenommen wird, dass das Datum now ( Q ) ist oder nur ein Datum wird angegeben ( Q ).

Ich möchte ein Datum und eine Uhrzeit angeben und dann eine Uhrzeit davon abziehen.
Folgendes habe ich zuerst versucht:

date -d "2018-12-10 00:00:00 - 5 hours - 20 minutes - 5 seconds"

Das führt zu 2018-12-10 06:39:55 - Es wurden 7 Stunden hinzugefügt. Dann 20:05 Minuten abgezogen.

Nachdem ich die Seiten man und info von date gelesen hatte, dachte ich, ich hätte es damit behoben:

date -d "2018-12-10T00:00:00 - 5 hours - 20 minutes - 5 seconds"

Aber das gleiche Ergebnis. Woher kommt es überhaupt die 7 Stunden?

Ich habe auch andere Daten ausprobiert, weil ich dachte, wir hätten an diesem Tag vielleicht 7200 Schaltsekunden, wer weiß, lol. Aber die gleichen Ergebnisse.

Noch ein paar Beispiele:

$ date -d "2018-12-16T00:00:00 - 24 hours" +%Y-%m-%d_%H:%M:%S
2018-12-17_02:00:00

$ date -d "2019-01-19T05:00:00 - 2 hours - 5 minutes" +%Y-%m-%d_%H:%M:%S
2019-01-19_08:55:00

Aber hier wird es interessant. Wenn ich die Zeit bei der Eingabe weglasse, funktioniert es gut:

$ date -d "2018-12-16 - 24 hours" +%Y-%m-%d_%H:%M:%S
2018-12-15_00:00:00

$ date -d "2019-01-19 - 2 hours - 5 minutes" +%Y-%m-%d_%H:%M:%S
2019-01-18_21:55:00

$ date --version
date (GNU coreutils) 8.30

Was vermisse ich?

Update: Ich habe am Ende ein Z hinzugefügt und das Verhalten geändert:

$ date -d "2019-01-19T05:00:00Z - 2 hours" +%Y-%m-%d_%H:%M:%S
2019-01-19_04:00:00

Ich bin trotzdem verwirrt. Es gibt nicht viel darüber in der GNU-Infoseite über Datum.

Ich vermute, dies ist ein Zeitzonenproblem, aber ich zitiere The Calendar Wiki on ISO 8601 :

Wenn keine UTC-Beziehungsinformationen mit einer Zeitdarstellung angegeben werden, wird angenommen, dass die Zeit in Ortszeit liegt.

Welches ist was ich will. Meine Ortszeit ist auch richtig eingestellt. Ich bin mir nicht sicher, warum das Datum in diesem einfachen Fall, in dem ich eine Datums- und Uhrzeitangabe mache und etwas davon abziehen möchte, überhaupt mit der Zeitzone in Konflikt geraten würde. Sollte es nicht zuerst die Stunden von der Datumszeichenfolge abziehen? Selbst wenn es zuerst in ein Datum konvertiert und dann subtrahiert wird, bekomme ich genau das, was ich will, wenn ich Subtraktionen weglasse:

$ date -d "2019-01-19T05:00:00" +%Y-%m-%d_%H:%M:%S
2019-01-19_05:00:00

Also [~ # ~] wenn [~ # ~] dies wirklich ein Zeitzonenproblem ist, woher kommt dieser Wahnsinn?

20
confetti

Das letzte Beispiel hätte die Dinge für Sie klarstellen sollen: Zeitzonen.

$ TZ=UTC date -d "2019-01-19T05:00:00Z - 2 hours" +%Y-%m-%d_%H:%M:%S
2019-01-19_03:00:00
$ TZ=Asia/Colombo date -d "2019-01-19T05:00:00Z - 2 hours" +%Y-%m-%d_%H:%M:%S 
2019-01-19_08:30:00

Da die Ausgabe je nach Zeitzone deutlich variiert, würde ich einen nicht offensichtlichen Standard für eine Zeitzeichenfolge ohne angegebene Zeitzone vermuten. Beim Testen einiger Werte scheint es TC-05: zu sein, obwohl ich nicht sicher bin, was das ist.

$ TZ=UTC date -d "2019-01-19T05:00:00 - 2 hours" +%Y-%m-%d_%H:%M:%S%Z
2019-01-19_08:00:00UTC
$ TZ=UTC date -d "2019-01-19T05:00:00Z - 2 hours" +%Y-%m-%d_%H:%M:%S%Z
2019-01-19_03:00:00UTC
$ TZ=UTC date -d "2019-01-19T05:00:00" +%Y-%m-%d_%H:%M:%S%Z           
2019-01-19_05:00:00UTC

Es wird nur beim Ausführen von Datumsarithmetik verwendet.


Es scheint, dass das Problem hier ist, dass - 2 hours wird nicht als Arithmetik genommen, aber als Zeitzonenspezifizierer :

# TZ=UTC date -d "2019-01-19T05:00:00 - 2 hours" +%Y-%m-%d_%H:%M:%S%Z --debug
date: parsed datetime part: (Y-M-D) 2019-01-19 05:00:00 UTC-02
date: parsed relative part: +1 hour(s)
date: input timezone: parsed date/time string (-02)
date: using specified time as starting value: '05:00:00'
date: starting date/time: '(Y-M-D) 2019-01-19 05:00:00 TZ=-02'
date: '(Y-M-D) 2019-01-19 05:00:00 TZ=-02' = 1547881200 Epoch-seconds
date: after time adjustment (+1 hours, +0 minutes, +0 seconds, +0 ns),
date:     new time = 1547884800 Epoch-seconds
date: timezone: TZ="UTC" environment value
date: final: 1547884800.000000000 (Epoch-seconds)
date: final: (Y-M-D) 2019-01-19 08:00:00 (UTC)
date: final: (Y-M-D) 2019-01-19 08:00:00 (UTC+00)
2019-01-19_08:00:00UTC

Es wird also nicht nur keine Arithmetik durchgeführt, es scheint auch eine zu geben sommerzeit 1 Stunde Zeitanpassung, was für uns zu einer etwas unsinnigen Zeit führt.

Dies gilt auch für die Ergänzung:

# TZ=UTC date -d "2019-01-19T05:00:00 + 5:30 hours" +%Y-%m-%d_%H:%M:%S%Z --debug
date: parsed datetime part: (Y-M-D) 2019-01-19 05:00:00 UTC+05:30
date: parsed relative part: +1 hour(s)
date: input timezone: parsed date/time string (+05:30)
date: using specified time as starting value: '05:00:00'
date: starting date/time: '(Y-M-D) 2019-01-19 05:00:00 TZ=+05:30'
date: '(Y-M-D) 2019-01-19 05:00:00 TZ=+05:30' = 1547854200 Epoch-seconds
date: after time adjustment (+1 hours, +0 minutes, +0 seconds, +0 ns),
date:     new time = 1547857800 Epoch-seconds
date: timezone: TZ="UTC" environment value
date: final: 1547857800.000000000 (Epoch-seconds)
date: final: (Y-M-D) 2019-01-19 00:30:00 (UTC)
date: final: (Y-M-D) 2019-01-19 00:30:00 (UTC+00)
2019-01-19_00:30:00UTC

Wenn Sie etwas mehr debuggen, scheint das Parsen zu sein: 2019-01-19T05:00:00 - 2 (-2 ist die Zeitzone) und hours (= 1 Stunde) mit einer impliziten Addition. Es wird einfacher zu erkennen, ob Sie stattdessen Minuten verwenden:

# TZ=UTC date -d "2019-01-19T05:00:00 - 2 minutes" +%Y-%m-%d_%H:%M:%S%Z --debug
date: parsed datetime part: (Y-M-D) 2019-01-19 05:00:00 UTC-02
date: parsed relative part: +1 minutes
date: input timezone: parsed date/time string (-02)
date: using specified time as starting value: '05:00:00'
date: starting date/time: '(Y-M-D) 2019-01-19 05:00:00 TZ=-02'
date: '(Y-M-D) 2019-01-19 05:00:00 TZ=-02' = 1547881200 Epoch-seconds
date: after time adjustment (+0 hours, +1 minutes, +0 seconds, +0 ns),
date:     new time = 1547881260 Epoch-seconds
date: timezone: TZ="UTC" environment value
date: final: 1547881260.000000000 (Epoch-seconds)
date: final: (Y-M-D) 2019-01-19 07:01:00 (UTC)
date: final: (Y-M-D) 2019-01-19 07:01:00 (UTC+00)
2019-01-19_07:01:00UTC

Nun, es wird eine Datumsarithmetik durchgeführt, nur nicht die, nach der wir gefragt haben. ¯\(ツ)/¯

20
Olorin

Es funktioniert ordnungsgemäß, wenn Sie zuerst das Eingabedatum in ISO 8601 konvertieren:

$ date -d "$(date -Iseconds -d "2018-12-10 00:00:00") - 5 hours - 20 minutes - 5 seconds"
So 9. Dez 18:39:55 CET 2018
6
pLumo

TLDR : Dies ist kein Fehler. Sie haben gerade eines der subtilen, aber dokumentierten Verhaltensweisen von date gefunden. Verwenden Sie beim Ausführen von Zeitarithmetik mit date ein zeitzonenunabhängiges Format (wie Unix-Zeit) oder lesen Sie die Dokumentation sorgfältig durch , um zu wissen, wie um diesen Befehl richtig zu verwenden.


GNU date verwendet Ihre Systemeinstellungen (die Umgebungsvariable TZ oder, falls nicht festgelegt, die Systemstandards), um die Zeitzone der beiden mit -d/--date Option und das vom Argument +format Angegebene Datum. Mit der Option --date Können Sie auch die Zeitzone für ein eigenes Optionsargument überschreiben, die Zeitzone von +format Wird jedoch nicht überschrieben. Das ist die Wurzel der Verwirrung. MEINER BESCHEIDENEN MEINUNG NACH.

Vergleichen Sie die folgenden Befehle, da meine Zeitzone UTC-6 ist:

$ date -d '1970-01-01 00:00:00' '+Normal: %F %T %:z%nUnix: %s'
Normal: 1970-01-01 00:00:00 -06:00
Unix: 21600
$ date -d '1970-01-01 00:00:00 UTC' '+Normal: %F %T %:z%nUnix: %s'
Normal: 1969-12-31 18:00:00 -06:00
Unix: 0
$ TZ='UTC0' date -d '1970-01-01 00:00:00 UTC' '+Normal: %F %T %:z%nUnix: %s'
Normal: 1970-01-01 00:00:00 +00:00
Unix: 0

Der erste verwendet meine Zeitzone sowohl für -d Als auch für +format. Der zweite verwendet UTC für -d, Aber meine Zeitzone für +format. Der dritte verwendet UTC für beide.

Vergleichen Sie nun die folgenden einfachen Operationen:

$ date -d '1970-01-01 00:00:00 UTC +1 day' '+Normal: %F %T %:z%nUnix: %s'
Normal: 1970-01-01 18:00:00 -06:00
Unix: 86400
$ TZ='UTC0' date -d '1970-01-01 00:00:00 UTC +1 day' '+Normal: %F %T %:z%nUnix: %s'
Normal: 1970-01-02 00:00:00 +00:00
Unix: 86400

Selbst wenn die Unix-Zeit mir dasselbe sagt, unterscheidet sich die "normale" Zeit aufgrund meiner eigenen Zeitzone.

Wenn ich den gleichen Vorgang ausführen wollte, aber ausschließlich meine Zeitzone verwenden wollte:

$ TZ='CST+6' date -d '1970-01-01 00:00:00 -06:00 +1 day' '+Normal: %F %T %:z%nUnix: %s'
Normal: 1970-01-02 00:00:00 -06:00
Unix: 108000
5
nxnev

GNU date unterstützt einfache Datumsarithmetik, obwohl Epochenzeitberechnungen, wie in der Antwort von @sudodus gezeigt, manchmal klarer (und portabler) sind.

Die Verwendung von +/-, wenn im Zeitstempel keine Zeitzone angegeben ist, löst einen Versuch aus, als nächstes eine Zeitzone abzugleichen, bevor etwas anderes analysiert wird.

Hier ist eine Möglichkeit, "vor" anstelle von "-" zu verwenden:

$ date -d "2018-12-10 00:00:00 5 hours ago 20 minutes ago 5 seconds ago"
Sun Dec  9 18:39:55 GMT 2018

oder

$ date -d "2018-12-10 00:00:00Z -5 hours -20 minutes -5 seconds"
Sun Dec  9 18:39:55 GMT 2018

(Obwohl Sie "Z" nicht willkürlich verwenden können, funktioniert es in meiner Zone, aber das macht es zu einem Zeitstempel für die UTC/GMT-Zone. Verwenden Sie Ihre eigene Zone oder% z /% Z, indem Sie ${TZ:-$(date +%z)} an den Zeitstempel anhängen stattdessen.)

Durch Hinzufügen zusätzlicher Zeitbedingungen für diese Formulare wird die Zeit angepasst:

  • "Vor 5 Stunden" subtrahieren 5 Stunden
  • "4 Stunden" addieren (implizit) 4 Stunden
  • "3 Stunden danach" 3 Stunden hinzufügen (explizit) (in älteren Versionen nicht unterstützt)

Viele komplexe Anpassungen in beliebiger Reihenfolge können verwendet werden (obwohl relative und variable Begriffe wie "14 Wochen also letzter Montag" Probleme bereiten ;-)

(Es gibt auch hier eine andere kleine Bärenfalle, date gibt immer ein gültiges Datum an, also gibt date -d "2019-01-31 1 month" 2019-03-03 an, ebenso wie "nächster Monat")

Angesichts der Vielzahl der unterstützten Zeit- und Datumsformate ist das Parsen der Zeitzone zwangsläufig schlampig: Es kann sich um ein Suffix aus einem oder mehreren Buchstaben, einen Stunden- oder Stunden-Minuten-Versatz, einen Namen "America/Denver" (oder sogar einen Dateinamen) handeln im Fall der Variablen TZ).

Ihre 2018-12-10T00:00:00 - Version funktioniert nicht, da "T" nur ein Trennzeichen und keine Zeitzone ist. Wenn Sie am Ende "Z" hinzufügen, funktioniert dies erwartungsgemäß auch (vorbehaltlich der Richtigkeit der ausgewählten Zone).

Siehe: https://www.gnu.org/software/tar/manual/html_node/Date-input-formats.html und insbesondere Abschnitt 7.7.

4
mr.spuratic

Diese Lösung ist leicht zu verstehen, aber etwas komplizierter, daher zeige ich sie als Shellscript.

  • konvertieren in 'Sekunden seit 1970-01-01 00:00:00 UTC'
  • addiere oder subtrahiere die Differenz
  • konvertieren Sie mit einer endgültigen date -Befehlszeile zurück in ein lesbares Format

Shell-Skript:

#!/bin/bash

startdate="2018-12-10 00:00:00"

ddif="0"          # days
diff="-5:-20:-5"  # hours:minutes:seconds

#-----------------------------------------------------------------------------

ss1970in=$(date -d "$startdate" "+%s")  # seconds since 1970-01-01 00:00:00 UTC
printf "%11s\n" "$ss1970in"

h=${diff%%:*}
m=${diff#*:}
m=${m%:*}
s=${diff##*:}
difs=$(( (((ddif*24+h)*60)+m)*60+s ))
printf "%11s\n" "$difs"

ss1970ut=$((ss1970in + difs))  # add/subtract the time difference
printf "%11s\n" "$ss1970ut"

date -d "@$ss1970ut" "+%Y-%m-%d %H:%M:%S"
1
sudodus