Berechnen Sie schnell Datumsunterschiede


84

Ich möchte oft einige schnelle Datumsberechnungen durchführen, wie zum Beispiel:

  • Was ist der Unterschied zwischen diesen beiden Daten?
  • Was ist das Datum n Wochen nach diesem anderen Datum?

Normalerweise öffne ich einen Kalender und zähle die Tage, aber ich denke, es sollte ein Programm / Skript geben, mit dem ich diese Art von Berechnungen durchführen kann. Irgendwelche Vorschläge?


3
Siehe auch Tool in UNIX zum Subtrahieren von Daten, wenn das GNU-Datum nicht verfügbar ist.
Gilles

Antworten:


106

Die "n Wochen nach einem Datum" sind mit GNU date (1) einfach:

$ date -d 'now + 3 weeks'
Tue Dec  6 23:58:04 EST 2011
$ date -d 'Aug 4 + 3 weeks'
Thu Aug 25 00:00:00 EST 2011
$ date -d 'Jan 1 1982 + 11 weeks'
Fri Mar 19 00:00:00 EST 1982

Ich kenne keine einfache Methode, um die Differenz zwischen zwei Daten zu berechnen, aber Sie können date (1) mit einer Shell-Funktion in eine kleine Logik umschließen.

datediff() {
    d1=$(date -d "$1" +%s)
    d2=$(date -d "$2" +%s)
    echo $(( (d1 - d2) / 86400 )) days
}
$ datediff '1 Nov' '1 Aug'
91 days

Tauschen Sie d1und d2wenn Sie die Datumsberechnung in die andere Richtung wollen, oder etwas ausgefeilter, damit es keine Rolle spielt. Darüber hinaus ist einer der Tage nur 23 Stunden lang, wenn in dem Intervall ein Übergang von der Sommerzeit zur Winterzeit stattfindet . Sie können dies kompensieren, indem Sie der Summe einen halben Tag hinzufügen.

echo $(( (((d1-d2) > 0 ? (d1-d2) : (d2-d1)) + 43200) / 86400 )) days

40

Probieren Sie für eine Reihe von tragbaren Tools meine eigenen Dateutils . Ihre beiden Beispiele würden sich auf Einzeiler beschränken:

ddiff 2011-11-15 2012-04-11
=>
  148

oder in Wochen und Tagen:

ddiff 2011-11-15 2012-04-11 -f '%w %d'
=>
  21 1

und

dadd 2011-11-15 21w
=>
  2012-04-10

3
+1 Ihre Werkzeuge rocken (obwohl dateadd -i '%m%d%Y' 01012015 +1des nicht zu funktionieren scheint, es hängt nur auf unbestimmte Zeit dort ... es funktioniert, wenn die Datumsangaben durch ein
Zeichen

2
@don_crissti Der Parser konnte nicht zwischen nur numerischen Daten und Dauern unterscheiden. Er ist im aktuellen Master (d0008f98)
hroptatyr 23.11.15

Haben Sie Windows Binärdistribution von Dateutils?
Mosh

@mosh nein, und ich habe nicht die Einrichtungen, um es zu versuchen.
hroptatyr

Spät zur Party hier, aber Dateutils ist auf Cygwin, wenn Sie es unter Windows brauchen.
Gregb212

30

Ein Python-Beispiel zur Berechnung der Anzahl der Tage, an denen ich den Planeten bereist habe:

$ python
>>> from datetime import date as D
>>> print (D.today() - D(1980, 6, 14)).days
11476

2
Nur für den Fall, dass jemand möchte, dass sich dies wie ein einzelner Befehl verhält, anstatt einen interaktiven Interpreter ychaouche@ychaouche-PC ~ $ python -c "from datetime import date as d; print (d.today() - d(1980, 6, 14)).days" 12813 ychaouche@ychaouche-PC ~ $
einzugeben

1
das funktioniert bei mir python3 -c "von datetime import date as d; print (d.today () - d (2016, 1, 9))" tage am ende sind nicht erforderlich
Kiran K Telukunta 14.10.16

13

Normalerweise bevorzuge ich die Angabe von Uhrzeit und Datum im Unix-Utime-Format (Anzahl der Sekunden seit Beginn der siebziger Jahre, UTC). Auf diese Weise läuft es immer auf einfache Subtraktion oder Addition von Sekunden hinaus.

Das Problem besteht normalerweise darin, ein Datum / eine Uhrzeit in dieses Format umzuwandeln.

In einer Shell-Umgebung / einem Skript können Sie es mit date '+%s' herunterladen. Zum Zeitpunkt des Schreibens ist die aktuelle Uhrzeit 1321358027.

Zum Vergleich mit 2011-11-04 (mein Geburtstag) date '+%s' -d 2011-11-04, nachgebend 1320361200. Subtrahieren: expr 1321358027 - 1320361200Gibt 996827Sekunden zurück, was expr 996827 / 86400= vor 11 Tagen ist.

Das Konvertieren von utime (1320361200-Format) in ein Datum ist mit Standardbibliotheken zum Beispiel in C, PHP oder Perl sehr einfach. Bei GNU datekann dem -dArgument @das Format "Sekunden seit der Epoche" vorangestellt werden.


7
date -d @1234567890Konvertiert mit GNU date von Sekunden seit der Epoche in das von Ihnen angegebene Datumsformat.
Gilles

1
@ Gilles: das ist genial. Konnte das nicht auf der Manpage finden. Wo hast du das gelernt?
MattBianco

1
@MattBianco, siehe info dateinsbesondere die Sekunden seit dem Knoten " Epoche ": "Wenn Sie einer Zahl ein" @ "voranstellen, stellt dies einen internen Zeitstempel als Anzahl von Sekunden dar."
manatwork

8

Dies trat auf, als verwendet wurde date -d "$death_date - $y years - $m months - $d days", um ein Geburtsdatum zu erhalten (für Genealogie). Dieser Befehl ist FALSCH. Monate sind also nicht alle gleich lang (date + offset) - offset != date. Das Alter in Jahr / Monat / Tag ist ein Maß, das vom Geburtsdatum an voranschreitet.

$ date --utc -d 'mar 28 1867 +72years +11months +2days'
Fri Mar  1 00:00:00 UTC 1940

$ date --utc -d 'mar 1 1940 -72years -11months -2days'
Sat Mar 30 00:00:00 UTC 1867
# (2 days later than our starting point)

Das Datum gibt in beiden Fällen die richtige Ausgabe aus, aber im zweiten Fall haben Sie die falsche Frage gestellt. Es ist wichtig, WELCHE 11 Monate im Jahr +/- 11 umfassen, bevor Tage addiert / subtrahiert werden. Zum Beispiel:

$ date --utc -d 'mar 31 1939  -1month'
Fri Mar  3 00:00:00 UTC 1939
$ date --utc -d 'mar 31 1940  -1month' # leap year
Sat Mar  2 00:00:00 UTC 1940
$ date --utc -d 'jan 31 1940  +1month' # leap year
Sat Mar  2 00:00:00 UTC 1940

Damit das Subtrahieren die inverse Operation des Addierens ist, müsste die Reihenfolge der Operationen umgekehrt werden. Durch Hinzufügen werden Jahre, DANN Monate, DANN Tage hinzugefügt. Wenn Sie die umgekehrte Reihenfolge beim Subtrahieren verwenden, kehren Sie zum Ausgangspunkt zurück. Dies ist nicht der Fall, und Sie tun es auch nicht, wenn der Tagversatz eine Monatsgrenze in einem Monat mit einer anderen Länge überschreitet.

Wenn Sie ab einem Enddatum und Alter rückwärts arbeiten müssen, können Sie dies mit mehreren Aufrufen von tun date. Subtrahieren Sie zuerst die Tage, dann die Monate und dann die Jahre. (Ich denke nicht, dass es sicher ist, die Jahre und Monate in einem einzigen dateAufruf zu kombinieren , da Schaltjahre die Länge des Februar verändern.)


7

Wenn ein grafisches Tool für Sie in Ordnung ist, empfehle ich es von ganzem Herzen qalculate(ein Taschenrechner mit Schwerpunkt auf Einheitenumrechnungen, mit GTK- und KDE-Schnittstelle, IIRC). Dort kann man zB sagen

days(1900-05-21, 1900-01-01)

Um die Anzahl der Tage (140, da 1900 kein Schaltjahr war) zwischen den Daten zu ermitteln, können Sie dies natürlich auch für die folgenden Zeiten tun:

17:12:45 − 08:45:12

ergibt 8.4591667Stunden oder, wenn Sie die Ausgabe Zeit Formatierung eingestellt 8:27:33.


3
Das ist toll, und noch mehr, weil qalculate hat eine CLI hat. Versuchen Sie es qalcdann help days.
Sparhawk

qcalcBeispiel: qalc -t 'days(1900-05-21, 1900-01-01)'-> 140
sierrasdetandil

3

Ich verwende häufig SQL für Datumsberechnungen. Zum Beispiel MySQL , PostgreSQL oder SQLite :

bash-4.2$ mysql <<< "select datediff(current_date,'1980-06-14')"
datediff(current_date,'1980-06-14')
11477

bash-4.2$ psql <<< "select current_date-'1980-06-14'"
 ?column? 
----------
    11477
(1 row)

bash-4.2$ sqlite2 <<< "select julianday('now')-julianday('1980-06-14');"
11477.3524537035

Manchmal habe ich einfach Lust auf JavaScript. Zum Beispiel SpiderMonkey , WebKit , Seed oder Node.js :

bash-4.2$ js -e 'print((new Date()-new Date(1980,5,14))/1000/60/60/24)'
11477.477526192131

bash-4.2$ jsc-1 -e 'print((new Date()-new Date(1980,5,14))/1000/60/60/24)'
11477.47757960648

bash-4.2$ seed -e '(new Date()-new Date(1980,5,14))/1000/60/60/24'
11477.4776318287

bash-4.2$ node -pe '(new Date()-new Date(1980,5,14))/1000/60/60/24'
11624.520061481482

(Achten Sie darauf, wenn Sie den Monat an den DateKonstruktor des JavaScript- Objekts übergeben. Beginnt mit 0.)


3

Eine andere Möglichkeit, die Differenz zwischen zwei Daten desselben Kalenderjahres zu berechnen, ist die folgende:

date_difference.sh
1  #!/bin/bash
2  DATEfirstnum=`date -d "2014/5/14" +"%j"`
3  DATElastnum=`date -d "12/31/14" +"%j"`
4  DAYSdif=$(($DATElastnum - $DATEfirstnum))
5  echo "$DAYSdif"
  • Zeile 1 gibt der Shell an, welcher Interpreter verwendet werden soll.
  • Zeile 2 weist dateder Variablen DATEfirstnum den Wert von aus zu. Das -dFlag zeigt die Zeichenfolge in einem Zeitformat an, in diesem Fall am 14. Mai 2014, und +"%j"weist datean, die Ausgabe nur auf den Tag des Jahres (1-365) zu formatieren.
  • Zeile 3 ist mit Zeile 2 identisch, hat jedoch ein anderes Datum und ein anderes Format für die Zeichenfolge (31. Dezember 2014).
  • Zeile 4 weist den Wert DAYSdifder Differenz der beiden Tage zu.
  • Zeile 5 zeigt den Wert von an DAYSdif.

Dies funktioniert mit der GNU-Version von date, jedoch nicht mit der PC-BSD / FreeBSD-Version. Ich habe coreutilsvon Ports Tree aus installiert und /usr/local/bin/gdatestattdessen den Befehl verwendet.


1
Dieses Skript wird nicht ausgeführt. In der letzten Zeile und in den Leerzeichen um die Variablenzuweisungen befindet sich ein Tippfehler. Daher versucht bash, ein Programm namens DATEfirst name mit zwei Argumenten auszuführen. Versuchen Sie Folgendes: DATEfirstnum=$(date -d "$1" +%s) DATElastnum=$(date -d "$2" +%s) Außerdem kann dieses Skript die Differenz zwischen zwei verschiedenen Jahren nicht berechnen. +%jbezieht sich auf den Tag des Jahres (001..366), ./date_difference.sh 12/31/2001 12/30/2014gibt also -1 aus. Wie andere Antworten angemerkt haben, müssen Sie beide Daten seit 1970-01-01 00:00:00 UTC in Sekunden umrechnen.
Sechs

Sie brauchen keinen $arithmetischen Ausdruck im Inneren: $((DATElastnum - DATEfirstnum))funktioniert auch.
Ruslan

3

Mit Hilfe von dannas solutions kann dies in einer Zeile mit folgendem Code erfolgen:

python -c "from datetime import date as d; print(d.today() - d(2016, 7, 26))"

(Funktioniert sowohl in Python 2.x als auch in Python 3.)


1
Sie können Ihren Beitrag bearbeiten, anstatt zu kommentieren
Stephen Rauch

3

dateund bash können Datumsunterschiede verursachen (OS X-Optionen werden angezeigt). Legen Sie das letztere Datum zuerst.

echo $((($(date -jf%D "04/03/16" +%s) - $(date -jf%D "03/02/16" +%s)) / 86400))
# 31

1

Es gibt auch die Zeitberechnungen der GNU-Einheit in Kombination mit dem GNU-Datum:

$ gunits $(gdate +%s)sec-$(gdate +%s -d -1234day)sec 'yr;mo;d;hr;min;s'
        3 yr + 4 mo + 16 d + 12 hr + 37 min + 26.751072 s
$ gunits $(gdate +%s -d '2015-1-2 3:45:00')sec-$(gdate +%s -d '2013-5-6 7:43:21')sec 'yr;mo;d;hr;min;s'
        1 yr + 7 mo + 27 d + 13 hr + 49 min + 26.206759 s

(gunits ist Einheiten in Linux, gdate ist Datum)


1

datediff.sh auf github: gist

#!/bin/bash
#Simplest calculator two dates difference. By default in days

# Usage:
# ./datediff.sh first_date second_date [-(s|m|h|d) | --(seconds|minutes|hours|days)]

first_date=$(date -d "$1" "+%s")
second_date=$(date -d "$2" "+%s")

case "$3" in
"--seconds" | "-s") period=1;;
"--minutes" | "-m") period=60;;
"--hours" | "-h") period=$((60*60));;
"--days" | "-d" | "") period=$((60*60*24));;
esac

datediff=$(( ($first_date - $second_date)/($period) ))
echo $datediff

1

Die Antwort von camh erledigt das meiste, aber wir können den Umgang mit Rundungen, Zeitzonen usw. verbessern. Außerdem erhalten wir zusätzliche Präzision und die Möglichkeit, unsere Einheiten auszuwählen:

datediff() {
#convert dates to decimal seconds since 1970-01-01 00:00:00 UTC
date1seconds=$(date +%s.%N -d "$date1")
date2seconds=$(date +%s.%N -d "$date2")

#Calculate time difference in various time units
timeseconds=$(printf "%0.8f\n" $(bc <<<"scale=9; ($date2sec-$date1sec)"))
timeminutes=$(printf "%0.8f\n" $(bc <<<"scale=9; ($date2sec-$date1sec)/60"))
  timehours=$(printf "%0.8f\n" $(bc <<<"scale=9; ($date2sec-$date1sec)/3600"))
   timedays=$(printf "%0.8f\n" $(bc <<<"scale=9; ($date2sec-$date1sec)/86400"))
  timeweeks=$(printf "%0.8f\n" $(bc <<<"scale=9; ($date2sec-$date1sec)/604800"))
}

-dgibt an, datedass wir ein Datum und eine Uhrzeit für die Konvertierung angeben. +%s.%NÄndert die Datums- und Uhrzeitangabe in Sekunden.nanosekunden seit 1970-01-01 00:00:00 UTC. bcberechnet die Differenz zwischen den beiden Zahlen und der Teilungsfaktor ergibt die verschiedenen Einheiten. printfFügt bei Bedarf eine 0 vor der Dezimalstelle hinzu ( bcnicht) und stellt sicher, dass die Rundung auf die nächste Stelle erfolgt ( bcnur Kürzungen). Sie könnten auch verwenden awk.

Nun lassen Sie uns dies mit einem flippigen Testfall ausführen,

date1='Tue Jul  9 10:18:04.031 PST 2020'
date2='Wed May  8 15:19:34.447 CDT 2019'
datediff "$date1" "$date2"

echo $timeseconds seconds
-36971909.584000000 seconds

echo $timeminutes minutes
-616198.493066667 minutes

echo $timehours hours
-10269.974884444 hours

echo $timedays days
-427.915620185 days

echo $timeweeks weeks
-61.130802884 weeks

Da die Länge eines Monats oder Jahres nicht immer gleich ist, gibt es dort keine einzige "richtige" Antwort, obwohl man heutzutage 365,24 Tage als vernünftige Annäherung verwenden könnte.


0

Sie können die awk Velour-Bibliothek verwenden :

velour -n 'print t_secday(t_utc("2017-4-12") - t_utc("2017-4-5"))'

Oder:

velour -n 'print t_secday(t_utc(ARGV[1]) - t_utc(ARGV[2]))' 2017-4-12 2017-4-5

Ergebnis:

7
Durch die Nutzung unserer Website bestätigen Sie, dass Sie unsere Cookie-Richtlinie und Datenschutzrichtlinie gelesen und verstanden haben.
Licensed under cc by-sa 3.0 with attribution required.