it-swarm.com.de

Berechnen Sie schnell Datumsunterschiede

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?

93
daniel kullmann

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

$ 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 das Datum (1) mit einer Shell-Funktion ein wenig logisch 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 d1 und d2 wenn Sie die Datumsberechnung andersherum wünschen oder etwas ausgefallener werden möchten, damit es keine Rolle spielt. Darüber hinaus ist einer der Tage nur 23 Stunden lang, falls in dem Intervall eine Nicht-Sommerzeit für den Übergang DST vorliegt. 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
113
camh

Versuchen 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

Wichtiger Hinweis zur Ubuntu-Installation:

Dieselben Dateutils sind als Ubuntu-Paket verfügbar und können daher über Sudo apt install dateutils installiert werden. Vor Befehlen muss jedoch ein Dateutils stehen. Präfix wie in dateutils.ddiff 2019-03-28 2019-05-16

43
hroptatyr

Ein python Beispiel für die 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
32
Daniel Näslund

Normalerweise bevorzuge ich die Uhrzeit/das Datum im Unix-Utime-Format (Anzahl der Sekunden seit der Epoche, als die siebziger Jahre begannen, UTC). Auf diese Weise läuft es immer auf eine 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 Shell-Skript können Sie es mit date '+%s' Abrufen. Zum Zeitpunkt des Schreibens ist die aktuelle Zeit 1321358027.

Zum Vergleich mit 2011-11-04 (mein Geburtstag) date '+%s' -d 2011-11-04, Was 1320361200 Ergibt. Subtrahieren: expr 1321358027 - 1320361200 Gibt 996827 Sekunden an, was expr 996827 / 86400 = Vor 11 Tagen ist.

Das Konvertieren von utime (1320361200-Format) in ein Datum ist sehr einfach, beispielsweise in C, PHP oder Perl, unter Verwendung von Standardbibliotheken. Mit GNU date, dem Argument -d Kann @ Vorangestellt werden, um das Format "Sekunden seit der Epoche" anzugeben.

14
MattBianco

Wenn ein grafisches Tool für Sie in Ordnung ist, empfehle ich von Herzen qalculate (ein Taschenrechner mit Schwerpunkt auf Einheitenumrechnungen, der mit einer GTK- und KDE-Schnittstelle (IIRC) ausgestattet ist). Dort kann man z.B.

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 bestimmte Zeiten tun:

17:12:45 − 08:45:12

ergibt 8.4591667 Stunden oder, wenn Sie die Ausgabe auf Zeitformatierung einstellen, 8:27:33.

8
quazgar

Dies trat auf, wenn date -d "$death_date - $y years - $m months - $d days" um ein Geburtsdatum zu erhalten (für Genealogie). Dieser Befehl ist FALSCH. Monate sind nicht alle gleich lang, also (date + offset) - offset != date. Das Alter in Jahr/Monat/Tag ist eine Maßnahme, die ab dem Geburtsdatum gilt.

$ 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)

Datum gibt in beiden Fällen die richtige Ausgabe, aber im zweiten Fall haben Sie die falsche Frage gestellt. Es ist wichtig, WELCHE 11 Monate im Jahr die +/- 11 abdecken, 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 das Subtrahieren in umgekehrter Reihenfolge erfolgt, kehren Sie zu Ihrem Ausgangspunkt zurück. Dies ist nicht der Fall, wenn der Versatz der Tage 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 date tun. Subtrahieren Sie zuerst die Tage, dann die Monate, dann die Jahre. (Ich denke nicht, dass es sicher ist, die Jahre und Monate in einem einzigen date -Aufruf zu kombinieren, da Schaltjahre die Länge des Februar verändern.)

8
Peter Cordes

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 in beiden Python 2.x und Python 3.))

4

Eine andere Möglichkeit, die Differenz zwischen zwei Daten desselben Kalenderjahres zu berechnen, ist 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 der Variablen DATEfirstnum den Wert von out of date zu. Das Flag -d zeigt die Zeichenfolge in diesem Fall in einem Zeitformat an, in diesem Fall am 14. Mai 2014, und +"%j" weist date an, die Ausgabe nur auf den Tag des Jahres (1-365) zu formatieren.
  • Zeile 3 ist dieselbe wie Zeile 2, jedoch mit einem anderen Datum und Format für die Zeichenfolge, 31. Dezember 2014.
  • Zeile 4 weist der Differenz der beiden Tage den Wert DAYSdif zu.
  • Zeile 5 zeigt den Wert von DAYSdif an.

Dies funktioniert mit der Version GNU von date, jedoch nicht mit der PC-BSD/FreeBSD-Version. Ich habe coreutils über den Ports-Baum installiert und den Befehl /usr/local/bin/gdate verwendet stattdessen.

4
Justin Holcomb

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 nur 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 Konstruktor des JavaScript-Objekts Date übergeben. Beginnt mit 0.)

3
manatwork

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

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

Dies ist ein ziemlich alter Thread, aber ich habe etwas Interessantes entdeckt. Ich stecke mit BusyBox (v1.19) auf einem eingebetteten System fest und war etwas enttäuscht, dass die superhandliche now - 10 days - Notation dort fehlschlägt. Tatsächlich ist für die Option -d Ein sehr begrenzter Satz von Eingabeformaten verfügbar.

Recognized TIME formats:
    hh:mm[:ss]
    [YYYY.]MM.DD-hh:mm[:ss]
    YYYY-MM-DD hh:mm[:ss]
    [[[[[YY]YY]MM]DD]hh]mm[.ss]

An einem Eingabedatum können jedoch einige Berechnungen durchgeführt werden.

Angesichts dieser Formate habe ich festgestellt, dass Sie den letzten Tag des Vormonats erhalten, wenn Sie den Tag des anzuzeigenden Datums auf Null setzen:

$ date -d 2020.03.00-12:00
Sat Feb 29 12:00:00 EST 2020 

Und die Eingabe negativer Zahlen geht noch weiter zurück:

$ date -d 2020.03.-10-12:00
Wed Feb 19 12:00:00 EST 2020

Kann jemand bestätigen, ob dies auch am GNU Datum?) Funktioniert (obwohl der now - 10 days Sehr viel besser ist)

1
Nate

Antwort von camh kümmert sich um das meiste, aber wir können uns verbessern, um mit Rundungen, Zeitzonen usw. umzugehen, und wir erhalten 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"))
}

-d teilt date mit, dass wir ein Datum und eine Uhrzeit für die Konvertierung angeben. +%s.%N ändert das Datum und die Uhrzeit in Sekunden.nanosekunden seit 1970-01-01 00:00:00 UTC. bc berechnet die Differenz zwischen den beiden Zahlen und der Teilungsfaktor ergibt die verschiedenen Einheiten. printf fügt bei Bedarf eine 0 vor der Dezimalstelle hinzu (bc nicht) und stellt sicher, dass die Rundung auf den nächsten Wert erfolgt (bc wird nur abgeschnitten). Sie können auch awk verwenden.

Lassen Sie uns dies nun mit einem funky 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.

1
TTT

Es gibt auch GNU Zeitberechnungen der Einheit kombiniert mit 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
Larry

dateiff.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
user178063

Dies ist ein ziemlich alter Thread, aber ich habe in BusyBox etwas Interessantes entdeckt, das zumindest das Addieren und Subtrahieren von N Zeiteinheiten von einem bekannten Datum betrifft. Nicht so sehr, um das Intervall zwischen zwei bekannten Daten in die andere Richtung zu bestimmen.

Ich stecke auf einem eingebetteten System mit BusyBox fest und die super praktische now - 10 days - Notation schlägt dort fehl. Tatsächlich ist für die Option -d Ein sehr begrenzter Satz von Eingabeformaten verfügbar.

$ busybox date --help
BusyBox v1.19.0 (2019-07-14 10:52:19 UTC) multi-call binary.

Usage: date [OPTIONS] [+FMT] [TIME]

Display time (using +FMT), or set time

        [-s,--set] TIME Set time to TIME
        -u,--utc        Work in UTC (don't convert to local time)
        -R,--rfc-2822   Output RFC-2822 compliant date string
        -I[SPEC]        Output ISO-8601 compliant date string
                        SPEC='date' (default) for date only,
                        'hours', 'minutes', or 'seconds' for date and
                        time to the indicated precision
        -r,--reference FILE     Display last modification time of FILE
        -d,--date TIME  Display TIME, not 'now'
        -D FMT          Use FMT for -d TIME conversion

Recognized TIME formats:
        hh:mm[:ss]
        [YYYY.]MM.DD-hh:mm[:ss]
        YYYY-MM-DD hh:mm[:ss]
        [[[[[YY]YY]MM]DD]hh]mm[.ss]

An einem Eingabedatum können jedoch einige Berechnungen durchgeführt werden. Angesichts dieser Formate habe ich festgestellt, dass das Setzen der Tagesnummer des Eingabedatums auf Null den letzten Tag des Vormonats ergibt:

$ date -d 2020.03.00-12:00
Sat Feb 29 12:00:00 EST 2020 

Außerdem subtrahiert die Eingabe negativer Zahlen immer wieder rückwärts, was ich sicherlich nicht erwartet hatte:

$ date -d 2020.03.-10-12:00
Wed Feb 19 12:00:00 EST 2020

Sie können also an Ihrem bekannten Datum schnell rechnen, um Werte zu generieren, die in die Option -d Im Aufruf von BusyBox date eingegeben werden sollen:

#!/bin/bash
#Other shells not tested.

start_year=2020
start_month=1
start_day=20

offset_year=-2
offset_month=4
offset_day=5

new_year=$(( $start_year + $offset_year ))
new_month=$(( $start_month + $offset_month ))
new_day=$(( $start_day + $offset_day ))

new_date$( busybox date -d $new_year.$new_month.$new_day-12:00 +%Y-%m-%d )
echo $new_date

Ausgabe:

2018-05-25

Kann jemand bestätigen, ob dies auch mit GNU, BSD oder MacOS date funktioniert? Ich weiß sicherlich zu schätzen, dass GNU now - 10 days Sehr viel besser ist, aber ich bin daran interessiert, dies so portabel wie möglich zu gestalten. Ich habe bestätigt, dass Android sehr unterschiedlich ist (keiner der beiden Ansätze funktioniert dort).

0
Nate

date -d ist spezifisch für GNU Datum und wird nicht von POSIX definiert:

https://pubs.opengroup.org/onlinepubs/9699919799/utilities/date.html

Wie andere bereits erwähnt haben, ist dies möglicherweise besser für eine geeignete Programmiersprache geeignet. Zum Beispiel PHP:

<?php
$o1 = date_create('2020-02-26');
$o2 = date_create('2020-02-16');
$o3 = date_diff($o2, $o1);
echo 'days: ', $o3->d, "\n";

Ergebnis:

days: 10
0
Steven Penny