Wie finde ich den Unterschied in Tagen zwischen zwei Daten?


82

A = 2002-20-10
B = 2003-22-11

Wie finde ich den Unterschied in Tagen zwischen zwei Daten?


5
Wie unten erklärt, aber Ihre Daten sind falsch herum: Sie müssen JJJJ-MM-TT sein
Peter Flynn

Viele Antworten verwenden die Option -d des Datums (GNU-). Ich möchte hinzufügen, dass dies NICHT Teil des POSIX-Datums ist, daher weniger portabel. Solange das OP nicht mit Unix-Distributionen wie Solaris, sondern nur mit "Common Linux" arbeitet, sollte er oder sie gut sein. Ein entsprechender Tag würde imho helfen.
Cadoiz

Antworten:


33

Wenn Sie über GNU verfügen date, können Sie die Darstellung eines beliebigen Datums drucken ( -dOption). In diesem Fall konvertieren Sie die Daten in Sekunden seit EPOCH, subtrahieren und dividieren Sie durch 24 * 3600.

Oder brauchen Sie einen tragbaren Weg?


1
@Tree: Ich glaube, es gibt keine tragbare Möglichkeit, Daten zu subtrahieren, ohne sie selbst zu implementieren - es ist nicht so schwer, das einzige, was von Interesse ist, sind Schaltjahre. Aber heutzutage möchte jeder Daten subtrahieren, wie es scheint: Schauen Sie sich unix.stackexchange.com/questions/1825/… an :)

14
Um Lazywebs willen, denke ich, dass dies das ist, was user332325 bedeutete:echo "( `date -d $B +%s` - `date -d $A +%s`) / (24*3600)" | bc -l
Pablo Mendes

2
Was bedeutet tragbar in diesem Zusammenhang?
Htellez

@htellez etwas, das auf mehreren Plattformen (Windows, Linux usw.)
funktioniert

Für Bash One Liner basierend auf dieser Antwort siehe: stackoverflow.com/a/49728059/1485527
jschnasse

64

Die Bash-Methode: Konvertieren Sie die Daten in das Format% y% m% d, und Sie können dies direkt über die Befehlszeile tun:

echo $(( ($(date --date="031122" +%s) - $(date --date="021020" +%s) )/(60*60*24) ))

9
Dazu müssen die Daten nicht unbedingt %y%m%dformatiert sein. Das Gnu-Datum akzeptiert beispielsweise das %Y-%m-%dvom OP verwendete Format. Im Allgemeinen ist ISO-8601 eine gute Wahl (und das OP-Format ist ein solches Format). Formate mit zweistelligen Jahren werden besser vermieden.
Mc0e

2
Für den Unterschied von heute kann man gerne verwenden days_diff=$(( (`date -d $B +%s` - `date -d "00:00" +%s`) / (24*3600) )). Beachten Sie, dass $days_diffdies eine Ganzzahl sein wird (dh keine Dezimalstellen)
Julien

1
Dies hängt von der dateinstallierten Variante ab . Es funktioniert für GNU date. Es funktioniert nicht mit POSIX date. Dies ist wahrscheinlich kein Problem für die meisten Leser, aber es ist erwähnenswert.
mc0e

Wenn Sie wirklich POSIX-Portabilität wünschen, überprüfen Sie dies: unix.stackexchange.com/a/7220/43835
mc0e

1
Nur um Verwirrung zu bekämpfen: Das Format von OP ist es nicht %Y-%m-%d , bis wir es einführen August 2.0und October 2.0das Format von OP ist %Y-%d-%m. Relevante xkcd: xkcd.com/1179
Konfetti

22

tl; dr

date_diff=$(( ($(date -d "2015-03-11 UTC" +%s) - $(date -d "2015-03-05 UTC" +%s)) / (60*60*24) ))

Achtung! Viele der hier aufgeführten Bash-Lösungen sind für Datumsbereiche unterteilt, die das Datum umfassen, an dem die Sommerzeit beginnt (sofern zutreffend). Dies liegt daran, dass das Konstrukt $ ((math)) eine 'Floor'- / Kürzungsoperation für den resultierenden Wert ausführt und nur die ganze Zahl zurückgibt. Lassen Sie mich veranschaulichen:

Die Sommerzeit begann am 8. März dieses Jahres in den USA. Verwenden wir also einen Datumsbereich, der Folgendes umfasst:

start_ts=$(date -d "2015-03-05" '+%s')
end_ts=$(date -d "2015-03-11" '+%s')

Mal sehen, was wir mit den doppelten Klammern bekommen:

echo $(( ( end_ts - start_ts )/(60*60*24) ))

Gibt '5' zurück.

Wenn wir dies mit 'bc' genauer tun, erhalten wir ein anderes Ergebnis:

echo "scale=2; ( $end_ts - $start_ts )/(60*60*24)" | bc

Gibt '5,95' zurück - die fehlende 0,05 ist die verlorene Stunde aus der Sommerzeitumschaltung.

Wie soll das richtig gemacht werden?
Ich würde vorschlagen, dies stattdessen zu verwenden:

printf "%.0f" $(echo "scale=2; ( $end_ts - $start_ts )/(60*60*24)" | bc)

Hier rundet der 'printf' das von 'bc' berechnete genauere Ergebnis ab und gibt uns den korrekten Datumsbereich von '6'.

Bearbeiten: Hervorheben der Antwort in einem Kommentar von @ hank-schultz unten, den ich in letzter Zeit verwendet habe:

date_diff=$(( ($(date -d "2015-03-11 UTC" +%s) - $(date -d "2015-03-05 UTC" +%s) )/(60*60*24) ))

Dies sollte auch Schaltsekundensicher sein, solange Sie immer das frühere Datum vom späteren subtrahieren, da Schaltsekunden immer nur zur Differenz beitragen - das Abschneiden rundet effektiv auf das richtige Ergebnis ab.


6
Wenn Sie stattdessen die Zeitzone sowohl für den Start als auch für das Ende angeben (und sicherstellen, dass sie gleich sind), haben Sie auch nicht mehr das folgende Problem : echo $(( ($(date --date="2015-03-11 UTC" +%s) - $(date --date="2015-03-05 UTC" +%s) )/(60*60*24) )), das 6 statt 5 zurückgibt.
Hank Schultz

1
Ah, einer der Antwortenden dachte über die Ungenauigkeiten der Grundlösung nach, die von anderen in Varianten wiederholt wurden. Daumen hoch! Wie wäre es mit Schaltsekunden? Es ist Schaltsekundensicher? Es gibt Präzedenzfälle, in denen Software innerhalb eines Tages ein Ergebnis liefert, wenn sie zu bestimmten Zeiten ausgeführt wird, und Probleme im Zusammenhang mit der Schaltsekunde .
Stéphane Gourichon

Woops, die "falsche" Berechnung, die Sie als Beispiel angeben, gibt hier korrekt 6 zurück (Ubuntu 15.04 AMD64, GNU-Datumsversion 8.23). Vielleicht ist Ihr Beispiel zeitzonenabhängig? Meine Zeitzone ist "Europa / Paris".
Stéphane Gourichon

Gleiches gutes Ergebnis für die "falsche" Berechnung mit GNU-Datum 2.0 auf MSYS, gleiche Zeitzone.
Stéphane Gourichon

1
@ StéphaneGourichon, die Änderung der Sommerzeit in Ihrer Zeitzone scheint am 29. März 2015 stattgefunden zu haben. Probieren Sie also einen Datumsbereich aus, der dies beinhaltet.
evan_b

20

Und in Python

$python -c "from datetime import date; print (date(2003,11,22)-date(2002,10,20)).days"
398

2
@ Mouviciel nicht wirklich. Python ist nicht immer vorhanden.
Mc0e

1
@ mc0e in diesem Zusammenhang ist immer nichts vorhanden
Sumudu

5
@Anubis das OP hat Tags als bashund angegeben shell, daher können wir davon ausgehen, dass es sich um ein * nix-System handelt. Innerhalb solcher Systeme echound datesind zuverlässig vorhanden, Python jedoch nicht.
mc0e

2
@ mc0e ja das macht Sinn. Obwohl Python2 in den meisten * nix-Systemen verfügbar ist, müssen wir bei Low-End-Geräten (eingebettet usw.) nur mit primären Tools überleben.
Sumudu

11

Das funktioniert bei mir:

A="2002-10-20"
B="2003-11-22"
echo $(( ($(date -d $B +%s) - $(date -d $A +%s)) / 86400 )) days

Druckt

398 days

Was ist los?

  1. Geben Sie eine gültige Zeitzeichenfolge in A und an B an
  2. Verwenden date -d diese , um Zeitzeichenfolgen zu verarbeiten
  3. Verwenden date %s diese , um Zeitzeichenfolgen seit 1970 in Sekunden umzuwandeln (Unix-Epoche).
  4. Verwenden Sie die Bash-Parametererweiterung um Sekunden zu subtrahieren
  5. Teilen Sie durch Sekunden pro Tag (86400 = 60 * 60 * 24), um die Differenz als Tage zu erhalten
  6. ! Die Sommerzeit wird nicht berücksichtigt! Siehe diese Antwort unter unix.stackexchange !

7

Wenn die Option -d in Ihrem System funktioniert, können Sie dies auf folgende Weise tun. Es gibt eine Einschränkung, die Schaltjahre nicht berücksichtigen würde, da ich 365 Tage pro Jahr in Betracht gezogen habe.

date1yrs=`date -d "20100209" +%Y`
date1days=`date -d "20100209" +%j`
date2yrs=`date +%Y`
date2days=`date +%j`
diffyr=`expr $date2yrs - $date1yrs`
diffyr2days=`expr $diffyr \* 365`
diffdays=`expr $date2days - $date1days`
echo `expr $diffyr2days + $diffdays`

7

Hier ist die MAC OS X-Version für Ihre Bequemlichkeit.

$ A="2002-20-10"; B="2003-22-11";
$ echo $(((`date -jf %Y-%d-%m $B +%s` - `date -jf %Y-%d-%m $A +%s`)/86400))

nJoy!


1
-bash: (-) / 86400: Syntaxfehler: Operand erwartet (Fehlertoken ist ") / 86400")
Cavalcade

1
@cavalcade - das ist, weil Sie nicht getan habenA="2002-20-10"; B="2003-22-11"
RAM237

Vielen Dank, obwohl es für diesen speziellen Fall funktioniert, würde ich vorschlagen, zu verwenden echo $(((`date -jf "%Y-%d-%m" "$B" +%s` - `date -jf "%Y-%d-%m" "$A" +%s`)/86400)), dh sowohl Formate als auch Variablen in doppelte Anführungszeichen zu setzen, andernfalls kann es bei einem anderen Datumsformat (z. B. %b %d, %Y/ Jul 5, 2017)
fehlschlagen

@ RAM237 Das ist , was ich für nicht aufpasst headsmack Nun entdeckt, dank
Kavalkade

5

Selbst wenn Sie kein GNU-Datum haben, haben Sie wahrscheinlich Perl installiert:

use Time::Local;
sub to_epoch {
  my ($t) = @_; 
  my ($y, $d, $m) = ($t =~ /(\d{4})-(\d{2})-(\d{2})/);
  return timelocal(0, 0, 0, $d+0, $m-1, $y-1900);
}
sub diff_days {
  my ($t1, $t2) = @_; 
  return (abs(to_epoch($t2) - to_epoch($t1))) / 86400;
}
print diff_days("2002-20-10", "2003-22-11"), "\n";

Dies ergibt 398.041666666667398 Tage und eine Stunde aufgrund der Sommerzeit.


Die Frage tauchte wieder in meinem Feed auf. Hier ist eine präzisere Methode mit einem gebündelten Perl-Modul

days=$(perl -MDateTime -le '
    sub parse_date { 
        @f = split /-/, shift;
        return DateTime->new(year=>$f[0], month=>$f[2], day=>$f[1]); 
    }
    print parse_date(shift)->delta_days(parse_date(shift))->in_units("days");
' $A $B)
echo $days   # => 398

2

Dies ist die einfachste Methode, mit der ich an Centos 7 arbeiten konnte:

OLDDATE="2018-12-31"
TODAY=$(date -d $(date +%Y-%m-%d) '+%s')
LINUXDATE=$(date -d "$OLDDATE" '+%s')
DIFFDAYS=$(( ($TODAY - $LINUXDATE) / (60*60*24) ))

echo $DIFFDAYS

2

Hier ist mein Arbeitsansatz mit zsh. Unter OSX getestet:

# Calculation dates
## A random old date
START_DATE="2015-11-15"
## Today's date
TODAY=$(date +%Y-%m-%d)

# Import zsh date mod
zmodload zsh/datetime

# Calculate number of days
DIFF=$(( ( $(strftime -r %Y-%m-%d $TODAY) - $(strftime -r %Y-%m-%d $START_DATE) ) / 86400 ))
echo "Your value: " $DIFF

Ergebnis:

Your value:  1577

Grundsätzlich verwenden wir die Funktion strftimereverse ( -r), um unsere Datumszeichenfolge wieder in einen Zeitstempel umzuwandeln, und führen dann unsere Berechnung durch.


1

Ich würde eine andere mögliche Lösung in Ruby einreichen. Sieht so aus, als wäre es das kleinste und sauberste, das es bisher gibt:

A=2003-12-11
B=2002-10-10
DIFF=$(ruby -rdate -e "puts Date.parse('$A') - Date.parse('$B')")
echo $DIFF

2
Er sucht einen Weg in Bash.
Cojones

2
Es gibt keine tragbare Möglichkeit, dies in der Shell selbst zu tun. Alle Alternativen verwenden bestimmte externe Programme (z. B. GNU dateoder eine Skriptsprache), und ich denke ehrlich, dass Ruby ein guter Weg ist, um hierher zu gelangen. Diese Lösung ist sehr kurz und verwendet keine nicht standardmäßigen Bibliotheken oder andere Abhängigkeiten. Tatsächlich denke ich, dass die Wahrscheinlichkeit, dass Ruby installiert wird, höher ist als bei einem GNU-Datum.
GreyCat

1

Eine andere Python-Version:

python -c "from datetime import date; print date(2003, 11, 22).toordinal() - date(2002, 10, 20).toordinal()"



1

Unter Unix sollten GNU-Daten installiert sein. Sie müssen nicht von Bash abweichen. Hier ist die aufgereihte Lösung für Tage, nur um die Schritte zu zeigen. Es kann vereinfacht und auf volle Daten erweitert werden.

DATE=$(echo `date`)
DATENOW=$(echo `date -d "$DATE" +%j`)
DATECOMING=$(echo `date -d "20131220" +%j`)
THEDAY=$(echo `expr $DATECOMING - $DATENOW`)

echo $THEDAY 

1

Dies setzt voraus, dass ein Monat 1/12 eines Jahres ist:

#!/usr/bin/awk -f
function mktm(datespec) {
  split(datespec, q, "-")
  return q[1] * 365.25 + q[3] * 365.25 / 12 + q[2]
}
BEGIN {
  printf "%d\n", mktm(ARGV[2]) - mktm(ARGV[1])
}


0

Angenommen, wir synchronisieren Oracle DB-Sicherungen manuell auf einer tertiären Festplatte. Dann möchten wir alte Backups auf dieser Festplatte löschen. Hier ist ein kleines Bash-Skript:

#!/bin/sh

for backup_dir in {'/backup/cmsprd/local/backupset','/backup/cmsprd/local/autobackup','/backup/cfprd/backupset','/backup/cfprd/autobackup'}
do

    for f in `find $backup_dir -type d -regex '.*_.*_.*' -printf "%f\n"`
    do

        f2=`echo $f | sed -e 's/_//g'`
        days=$(((`date "+%s"` - `date -d "${f2}" "+%s"`)/86400))

        if [ $days -gt 30 ]; then
            rm -rf $backup_dir/$f
        fi

    done

done

Passen Sie die Verzeichnisse und die Aufbewahrungsfrist ("30 Tage") an Ihre Bedürfnisse an.


Oracle legt Sicherungssätze im Flash-Wiederherstellungsbereich im Format "JJJJ_MM_TT" ab, sodass wir die Unterstriche löschen, um sie an "Datum -d" zu übergeben
Alex Cherkas

0

Für MacOS Sierra (möglicherweise von Mac OS X Yosemate),

So rufen Sie die Epochenzeit (Sekunden ab 1970) aus einer Datei ab und speichern sie in einer Variablen: old_dt=`date -j -r YOUR_FILE "+%s"`

Um die Epochenzeit der aktuellen Zeit zu erhalten new_dt=`date -j "+%s"`

Berechnung der Differenz von mehr als zwei Epochen (( diff = new_dt - old_dt ))

Um zu überprüfen, ob diff länger als 23 Tage ist (( new_dt - old_dt > (23*86400) )) && echo Is more than 23 days


0
echo $(date +%d/%h/%y) date_today
echo " The other date is 1970/01/01"
TODAY_DATE="1970-01-01"
BEFORE_DATE=$(date +%d%h%y)
echo "THE DIFFERNS IS " $(( ($(date -d $BEFORE_DATE +%s) - $(date -d $TODAY_DATE +%s)) )) SECOUND

Verwenden Sie es, um Ihr Datum heute und Ihre Sekunde seit dem gewünschten Datum zu erhalten. Wenn Sie es mit Tagen wollen, teilen Sie es einfach mit 86400

echo $(date +%d/%h/%y) date_today
echo " The other date is 1970/01/01"
TODAY_DATE="1970-01-01"
BEFORE_DATE=$(date +%d%h%y)
echo "THE DIFFERNS IS " $(( ($(date -d $BEFORE_DATE +%s) - $(date -d $TODAY_DATE +%s))/ 86400)) SECOUND
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.