792 Stimmen

Ermitteln der Anzahl der Tage zwischen zwei Daten

Wie findet man die Anzahl der Tage zwischen zwei Daten mit PHP?

1105voto

Adnan Punkte 24838
$now = time(); // or your date as well
$your_date = strtotime("2010-01-31");
$datediff = $now - $your_date;

echo round($datediff / (60 * 60 * 24));

703voto

Saksham Gupta Punkte 6259

Wenn Sie Folgendes verwenden PHP 5.3 > Dies ist die bei weitem genaueste Methode zur Berechnung der absoluten Differenz:

$earlier = new DateTime("2010-07-06");
$later = new DateTime("2010-07-09");

$abs_diff = $later->diff($earlier)->format("%a"); //3

Wenn Sie eine relative (vorzeichenbehaftete) Anzahl von Tagen benötigen, verwenden Sie stattdessen diese Angabe:

$earlier = new DateTime("2010-07-06");
$later = new DateTime("2010-07-09");

$pos_diff = $earlier->diff($later)->format("%r%a"); //3
$neg_diff = $later->diff($earlier)->format("%r%a"); //-3

Mehr über php's DateInterval Format ist hier zu finden: https://www.php.net/manual/en/dateinterval.format.php

199voto

Aditya P Bhatt Punkte 20261

Ab PHP Version 5.3 und höher, neue Datum/Uhrzeit-Funktionen wurden hinzugefügt, um einen Unterschied zu erhalten:

$datetime1 = new DateTime("2010-06-20");

$datetime2 = new DateTime("2011-06-22");

$difference = $datetime1->diff($datetime2);

echo 'Difference: '.$difference->y.' years, ' 
                   .$difference->m.' months, ' 
                   .$difference->d.' days';

print_r($difference);

Ergebnis wie unten:

Difference: 1 years, 0 months, 2 days

DateInterval Object
(
    [y] => 1
    [m] => 0
    [d] => 2
    [h] => 0
    [i] => 0
    [s] => 0
    [invert] => 0
    [days] => 367
)

Ich hoffe, es hilft!

187voto

LSerni Punkte 53587

TL;DR tun pas UNIX-Zeitstempel verwenden. Nicht verwenden time() . Wenn Sie das tun, vorbereitet sein sollte seine 98,0825%ige Zuverlässigkeit Sie im Stich lassen. Verwenden Sie DateTime (oder Carbon).

El richtige Antwort ist die von Saksham Gupta gegebene Antwort (andere Antworten sind ebenfalls richtig):

$date1 = new DateTime('2010-07-06');
$date2 = new DateTime('2010-07-09');
$days  = $date2->diff($date1)->format('%a');

Oder verfahrenstechnisch als Einzeiler:

/**
 * Number of days between two dates.
 *
 * @param date $dt1    First date
 * @param date $dt2    Second date
 * @return int
 */
function daysBetween($dt1, $dt2) {
    return date_diff(
        date_create($dt2),  
        date_create($dt1)
    )->format('%a');
}

Mit einem Vorbehalt: Das "%a" scheint auf die absolut Anzahl der Tage. Wenn Sie sie als vorzeichenbehaftete Ganzzahl haben wollen, d.h. negativ, wenn das zweite Datum vor dem ersten liegt, dann müssen Sie das Präfix '%r' verwenden (d.h. format('%r%a') ).


Wenn Sie unbedingt UNIX-Zeitstempel verwenden müssen, die Zeitzone auf GMT einstellen zu vermeiden Die meisten der unten aufgeführten Fallstricke.


Lange Antwort: Warum die Division durch 24*60*60 (aka 86400) unsicher ist

Die meisten Antworten, die UNIX-Zeitstempel (und 86400 zur Umrechnung in Tage) verwenden, gehen von zwei Annahmen aus, die zusammengenommen zu Szenarien führen können mit falsche Ergebnisse y subtile Bugs die unter Umständen schwer zu erfassen sind und auch Tage, Wochen oder Monate nach einem erfolgreichen Einsatz noch auftreten können. Es geht nicht darum, dass die Lösung nicht funktioniert - sie funktioniert. Heute noch. Aber vielleicht funktioniert sie morgen nicht mehr.

Der erste Fehler ist, nicht zu bedenken, dass ein Computer auf die Frage "Wie viele Tage sind seit gestern vergangen?" wahrheitsgemäß antworten könnte Null wenn zwischen der Gegenwart und dem mit "gestern" bezeichneten Zeitpunkt weniger als einen ganzen Tag ist vorbei.

Wenn man einen "Tag" in einen UNIX-Zeitstempel umwandelt, erhält man normalerweise den Zeitstempel für die Mitternacht des jeweiligen Tages.

Zwischen den Mitternächten des 1. und des 15. Oktobers sind also fünfzehn Tage verstrichen. Aber zwischen 13:00 Uhr am 1. Oktober und 14:55 Uhr am 15. Oktober, fünfzehn Tage minus 5 Minuten verstrichen sind, und die meisten Lösungen mit floor() oder eine implizite Integer-Konvertierung wird einen Tag weniger als erwartet melden .

Die Frage "Vor wie vielen Tagen war Y-m-d H:i:s"? ergibt also die falsche Antwort .

Der zweite Fehler ist die Gleichsetzung von einem Tag mit 86400 Sekunden. Dies ist fast immer Stimmt - es passiert oft genug, um die Zeiten zu übersehen, in denen es nicht passiert. Aber der Abstand in Sekunden zwischen zwei aufeinanderfolgenden Mitternachtsstunden ist sicher nicht 86400 mindestens zweimal im Jahr, wenn die Sommerzeit ins Spiel kommt. Der Vergleich zweier Daten über eine Sommerzeit-Grenze hinweg ergibt die falsche Antwort.

Selbst wenn Sie also den "Hack" verwenden, alle Datumszeitstempel auf eine feste Stunde, z.B. Mitternacht, zu setzen (dies wird auch implizit von verschiedenen Sprachen und Frameworks gemacht, wenn Sie nur Tag-Monat-Jahr und nicht auch Stunde-Minute-Sekunde angeben; dasselbe passiert mit dem DATE-Typ in Datenbanken wie MySQL), wird die weit verbreitete Formel

 FLOOR((unix_timestamp(DATE2) - unix_timestamp(DATE1)) / 86400)

o

 floor((time() - strtotime($somedate)) / 86400)

gibt z.B. 17 zurück, wenn DATE1 und DATE2 im gleichen Sommerzeitsegment des Jahres liegen; aber selbst wenn der Teil Stunde:Minute:Sekunde identisch ist, könnte das Argument 17.042 lauten, und schlimmer noch, 16.958, wenn sie in verschiedenen Sommerzeitsegmenten liegen und die Zeitzone die Sommerzeit kennt. Die Verwendung von floor() oder eine implizite Trunkierung zu einer Ganzzahl wird dann das, was eine 17 hätte sein sollen, in eine 16 umwandeln. Unter anderen Umständen werden Ausdrücke wie "$days > 17" Folgendes zurückgeben true für 17.042, auch wenn dies so aussieht, als ob die Zahl der verstrichenen Tage 18 beträgt.

Und die Dinge werden noch hässlicher, da dieser Code ist nicht tragbar plattformübergreifend, denn einige von ihnen gelten für Schaltsekunden, andere nicht . Auf diesen Plattformen, die tun ist die Differenz zwischen zwei Daten nicht 86400, sondern 86401 oder vielleicht 86399. Ein Code, der im Mai funktionierte und alle Tests bestanden hat, wird also im Juni nicht mehr funktionieren, wenn 12,99999 Tage als 12 statt 13 Tage angesehen werden. Zwei Daten, die 2015 funktionierten, werden 2017 nicht funktionieren - die dieselbe Daten, und keines der beiden Jahre ist ein Schaltjahr. Und zwischen dem 01.03.2018 und dem 01.03.2017, auf den Plattformen, die das interessiert, 366 Tage anstelle von 365 Tagen vergangen sein, was 2018 zu einem Schaltjahr macht (was es nicht ist).

Wenn Sie also wirklich UNIX-Zeitstempel verwenden wollen:

  • verwenden. round() weise funktionieren, nicht floor() .

  • alternativ: keine Berechnung der Differenzen zwischen D1-M1-YYY1 und D2-M2-YYY2. Diese Daten werden dann wirklich als D1-M1-JJJ1 00:00:00 und D2-M2-JJJ2 00:00:00 betrachtet. Konvertieren Sie stattdessen zwischen D1-M1-JJJ1 22:30:00 und D2-M2-JJJ2 04:30:00. Sie werden siempre eine Restzeit von etwa zwanzig Stunden erhalten. Daraus können einundzwanzig Stunden oder neunzehn und vielleicht achtzehn Stunden, neunundfünfzig Minuten und sechsunddreißig Sekunden werden. Das spielt keine Rolle. Es ist eine große Marge die auf absehbare Zeit positiv bleiben werden. Ahora können Sie ihn mit floor() in Sicherheit.

El richtig Die Lösung, um magische Konstanten, Rundungsfehler und Wartungsaufwand zu vermeiden, ist jedoch

  • Verwenden Sie eine Zeitbibliothek (Datetime, Carbon, was auch immer); entwickeln Sie nicht Ihre eigene

  • schreiben. umfangreiche Testfälle die Verwendung wirklich böser Datumswahlen - über Sommerzeitgrenzen hinweg, über Schaltjahre hinweg, über Schaltsekunden hinweg und so weiter, aber auch über gewöhnliche Daten. Idealerweise (Aufrufe von datetime sind rapide !) erzeugen vier ganze Jahre (und ein Tag) von Daten, indem Sie sie nacheinander aus Zeichenketten zusammensetzen und sicherstellen, dass die Differenz zwischen dem ersten Tag und dem zu testenden Tag stetig um eins zunimmt. Damit wird sichergestellt, dass bei Änderungen in den Low-Level-Routinen und Schaltsekunden behebt versuchen, Schaden anzurichten, werden Sie zumindest conozca .

  • führen Sie diese Tests regelmäßig zusammen mit dem Rest der Testsuite durch. Sie sind eine Sache von Millisekunden und können Ihnen buchstäblich Stunden des Kopfkratzens.


Wie auch immer Ihre Lösung aussieht, testen Sie sie!

Die Funktion funcdiff Im Folgenden wird eine der Lösungen (zufälligerweise die akzeptierte) in einem realen Szenario umgesetzt.

<?php
$tz         = 'Europe/Rome';
$yearFrom   = 1980;
$yearTo     = 2020;
$verbose    = false;

function funcdiff($date2, $date1) {
    $now        = strtotime($date2);
    $your_date  = strtotime($date1);
    $datediff   = $now - $your_date;
    return floor($datediff / (60 * 60 * 24));
}
########################################

date_default_timezone_set($tz);
$failures   = 0;
$tests      = 0;

$dom = array ( 0, 31, 28, 31, 30,
                  31, 30, 31, 31,
                  30, 31, 30, 31 );
(array_sum($dom) === 365) || die("Thirty days hath September...");
$last   = array();
for ($year = $yearFrom; $year < $yearTo; $year++) {
    $dom[2] = 28;
    // Apply leap year rules.
    if ($year % 4 === 0)   { $dom[2] = 29; }
    if ($year % 100 === 0) { $dom[2] = 28; }
    if ($year % 400 === 0) { $dom[2] = 29; }
    for ($month = 1; $month <= 12; $month ++) {
        for ($day = 1; $day <= $dom[$month]; $day++) {
            $date = sprintf("%04d-%02d-%02d", $year, $month, $day);
            if (count($last) === 7) {
                $tests ++;
                $diff = funcdiff($date, $test = array_shift($last));
                if ((double)$diff !== (double)7) {
                    $failures ++;
                    if ($verbose) {
                        print "There seem to be {$diff} days between {$date} and {$test}\n";
                    }
                }
            }
            $last[] = $date;
        }
    }
}

print "This function failed {$failures} of its {$tests} tests";
print " between {$yearFrom} and {$yearTo}.\n";

Das Ergebnis ist,

This function failed 280 of its 14603 tests

Horrorgeschichte: Die Kosten des "Zeitsparens"

Alles begann Ende 2014. Ein findiger Programmierer beschloss, bei einer Berechnung, die höchstens dreißig Sekunden dauerte, einige Mikrosekunden einzusparen, indem er an mehreren Stellen den berüchtigten Code "(MidnightOfDateB-MidnightOfDateA)/86400" einfügte. Es war eine so offensichtliche Optimierung, dass er sie nicht einmal dokumentierte, und die Optimierung bestand die Integrationstests und schlummerte irgendwie mehrere Monate lang unbemerkt im Code.

Dies geschah in einem Programm, das die Löhne für mehrere umsatzstarke Verkäufer berechnet, von denen der kleinste erschreckend viel mehr Einfluss hat als ein ganzes bescheidenes fünfköpfiges Programmierer-Team zusammen. Am 28. März 2015, in der Sommerzeitzone, schlug der Fehler zu - und algunos dieser Leute einen ganzen Tag lang um fette Provisionen gebracht. Erschwerend kam hinzu, dass die meisten von ihnen sonntags nicht arbeiteten und diesen Tag, der sich dem Monatsende näherte, nutzten, um ihre Rechnungen zu begleichen. Sie waren definitiv pas amüsiert.

Viel schlimmer ist, dass sie das (ohnehin geringe) Vertrauen in das Programm verloren haben. pas die darauf abzielen, sie heimlich zu betrügen, und vorgeben - und erhalten - eine vollständige, detaillierte Codeüberprüfung mit durchgeführten und kommentierten Testfällen in Laiensprache (sowie eine Menge roter Teppich in den folgenden Wochen).

Was soll ich sagen: Auf der positiven Seite sind wir eine Menge technischer Schulden losgeworden und konnten mehrere Teile eines Spaghetti-Durcheinanders, das auf einen COBOL-Befall in den Swinging '90s zurückging, neu schreiben und refaktorisieren. Das Programm läuft jetzt zweifellos besser, und es gibt viel mehr Debugging-Informationen, mit denen man schnell eingreifen kann, wenn etwas verdächtig aussieht. Ich schätze, dass allein diese eine Sache in absehbarer Zukunft vielleicht ein oder zwei Manntage pro Monat einsparen wird, so dass die Katastrophe wird haben einen silbernen oder sogar goldenen Hintergrund.

Auf der anderen Seite hat der ganze Wirbel das Unternehmen rund 200.000 € gekostet - plus Gesicht, plus zweifellos etwas Verhandlungsmacht (und damit noch mehr Geld).

Der für die "Optimierung" verantwortliche Mitarbeiter hatte im Dezember 2014, also lange vor der Katastrophe, den Job gewechselt, aber es wurde trotzdem darüber gesprochen, ihn auf Schadensersatz zu verklagen. Und es kam bei den oberen Rängen nicht gut an, dass es "die Schuld des Letzten" war - es sah aus wie ein abgekartetes Spiel, damit wir uns aus der Affäre ziehen konnten, und am Ende blieben wir für den Rest des Jahres in der Klemme, und einer aus dem Team kündigte am Ende des Sommers.

In neunundneunzig von hundert Fällen funktioniert der "86400-Hack" einwandfrei. (Zum Beispiel in PHP, strtotime() ignoriert die Sommerzeit und meldet, dass zwischen den Mitternachtsstunden des letzten Samstags im Oktober und denen des folgenden Montags genau 2 * 24 * 60 * 60 Sekunden vergangen sind, auch wenn dies eindeutig nicht wahr ... und zwei Fehler machen gerne einen richtig).

Dies, meine Damen und Herren, war un wenn dies nicht der Fall war. Wie bei Airbags und Sicherheitsgurten, werden Sie vielleicht nie vraiment brauchen die Komplexität (und Benutzerfreundlichkeit) von DateTime o Carbon . Aber der Tag, an dem Sie puede (oder der Tag, an dem Sie beweisen Sie haben darüber nachgedacht) wird wie ein Dieb in der Nacht kommen (wahrscheinlich um 02:00 Uhr an einem Sonntag im Oktober). Seien Sie vorbereitet.

153voto

Tatu Ulmanen Punkte 119424

Konvertieren Sie Ihre Daten in Unix-Zeitstempel, und subtrahieren Sie dann die eine von der anderen. So erhalten Sie die Differenz in Sekunden, die Sie durch 86400 (Anzahl der Sekunden in einem Tag) teilen, um die ungefähre Anzahl der Tage in diesem Bereich zu ermitteln.

Wenn Ihre Daten im Format 25.1.2010 , 01/25/2010 o 2010-01-25 können Sie die strtotime Funktion:

$start = strtotime('2010-01-25');
$end = strtotime('2010-02-20');

$days_between = ceil(abs($end - $start) / 86400);

使用方法 ceil rundet die Anzahl der Tage auf den nächsten vollen Tag auf. verwenden floor wenn Sie die Anzahl der vollen Tage zwischen diesen beiden Daten erhalten möchten.

Wenn Ihre Daten bereits im Unix-Zeitstempelformat vorliegen, können Sie die Konvertierung überspringen und einfach die $days_between Teil. Für exotischere Datumsformate müssen Sie möglicherweise einige benutzerdefinierte Analysen durchführen, um es richtig zu machen.

CodeJaeger.com

CodeJaeger ist eine Gemeinschaft für Programmierer, die täglich Hilfe erhalten..
Wir haben viele Inhalte, und Sie können auch Ihre eigenen Fragen stellen oder die Fragen anderer Leute lösen.

Powered by:

X