Berechnen Sie die Anzahl der Stunden zwischen zwei Daten in PHP

107

Wie berechne ich die Differenz zwischen zwei Daten in Stunden?

Beispielsweise:

day1=2006-04-12 12:30:00
day2=2006-04-14 11:30:00

In diesem Fall sollte das Ergebnis 47 Stunden betragen.

Denker
quelle
1
Meine erste Antwort wäre gewesen, beide Werte mit Zeitstempeln in Zeitstempel umzuwandeln strftime()und die Differenz durch 3600 zu teilen. Aber wird das immer funktionieren? Verdammt, Sommerzeit!
Pekka
@ Pekka: Nein, es wird nicht immer funktionieren, denke ich ... Schau dir meine Antwort an. Dort habe ich eine Lösung veröffentlicht, die Zeitzonen, Schaltjahre, Schaltsekunden und dst berücksichtigt :)
Fidi
@ Pekka, wenn Sie es verwenden strtotime(), wird immer funktionieren, solange Sie die Standardzeitzone verwenden ODER den Zeitzonenversatz explizit angeben. Kein Grund, die Sommerzeit zu verfluchen.
Walter Tross

Antworten:

205

Die neueren PHP-Versionen bieten einige neue Klassen genannt DateTime, DateInterval, DateTimeZoneund DatePeriod. Das Coole an diesen Kursen ist, dass sie unterschiedliche Zeitzonen, Schaltjahre, Schaltsekunden, Sommerzeit usw. berücksichtigen. Außerdem ist es sehr einfach zu bedienen. Mit Hilfe dieser Objekte möchten Sie Folgendes:

// Create two new DateTime-objects...
$date1 = new DateTime('2006-04-12T12:30:00');
$date2 = new DateTime('2006-04-14T11:30:00');

// The diff-methods returns a new DateInterval-object...
$diff = $date2->diff($date1);

// Call the format method on the DateInterval-object
echo $diff->format('%a Day and %h hours');

Das zurückgegebene DateInterval-Objekt bietet auch andere Methoden als format. Wenn Sie das Ergebnis nur in Stunden wollen, können Sie so etwas tun:

$date1 = new DateTime('2006-04-12T12:30:00');
$date2 = new DateTime('2006-04-14T11:30:00');

$diff = $date2->diff($date1);

$hours = $diff->h;
$hours = $hours + ($diff->days*24);

echo $hours;

Und hier sind die Links zur Dokumentation:

Alle diese Klassen bieten auch eine prozedurale / funktionale Möglichkeit, mit Daten zu arbeiten. Schauen Sie sich daher die Übersicht an: http://php.net/manual/book.datetime.php

Fidi
quelle
+1 Gute Arbeit! Das sieht solide aus und ist eine gute Übersicht. Es ist wichtig zu beachten, dass die Berechnungen aufgrund unterschiedlicher Sommerzeitregeln je nach Zeitzone variieren können. Daher ist es wahrscheinlich eine gute Idee, die Zone immer zu definieren und sich nicht auf Servereinstellungen zu verlassen.
Pekka
Ja. Mit diesem Objekt können Sie sogar zwischen Daten in verschiedenen Zeitzonen rechnen. $date1 = new DateTime('2006-04-12T12:30:00 Europe/Berlin');und$date2 = new DateTime('2006-04-14T11:30:00 America/New_York');
Fidi
3
Wenn jemand auf dasselbe Problem stößt wie ich, bei dem $diff->d0 gleich ist (weil ich versuche, die Stunden zwischen zwei Daten zu berechnen, die genau 2 Monate voneinander entfernt sind): Beim Laufen wurde var_dump($diff)mir ein anderer Parameter angezeigt : ["days"]=>int(61), also habe ich ihn verwendet $hours = $diff->days * 24;, und er kam in der Nähe des "Durchschnitts" von 1440 Stunden bei 2 30-Tage-Monaten, das sieht also viel besser aus als das Ergebnis von 0. (Ich schätze, meine PHP-Version ist ein bisschen alt ...)
Semmelbroesel
2
Ich meine, in vielen Teilen der Welt hat ein Jahr einen 23-Stunden-Tag und einen 25-Stunden-Tag.
Walter Tross
4
@Amal Murali, also hast du beschlossen, den Bonus auf diese Antwort zu vergeben, die FALSCH ist? Haben Sie versucht, mit dieser Antwort die Anzahl der Stunden zwischen dem Mittag des 1. Januar und dem Mittag des 1. Juni in einer Zeitzone mit Sommerzeit (Sommerzeit) zu berechnen? Sie erhalten ein gerades Ergebnis, während das wahre Ergebnis ungerade ist.
Walter Tross
78
$t1 = strtotime( '2006-04-14 11:30:00' );
$t2 = strtotime( '2006-04-12 12:30:00' );
$diff = $t1 - $t2;
$hours = $diff / ( 60 * 60 );
Jan Hančič
quelle
4
Warum nicht $diff / 3600?
Alex G
4
@AlexG Es ist nur eine Stilsache. Gleiche Ausgabe, aber Programmierer verwenden normalerweise Multiplikation, wenn es um Zeiten geht
Benutzer
Ich empfehle Ihnen zu mögen: rund (($ t1 - $ 22) / 3600); Verwenden Sie Runde, um korrekte Stunden zu erhalten
Shiv Singh
20

Bereitstellung einer anderen Methode für die DatePeriodVerwendung der UTC- oder GMT- Zeitzone.

Stunden zählen https://3v4l.org/Mu3HD

$start = new \DateTime('2006-04-12T12:30:00');
$end = new \DateTime('2006-04-14T11:30:00');

//determine what interval should be used - can change to weeks, months, etc
$interval = new \DateInterval('PT1H');

//create periods every hour between the two dates
$periods = new \DatePeriod($start, $interval, $end);

//count the number of objects within the periods
$hours = iterator_count($periods);
echo $hours . ' hours'; 

//difference between Unix Epoch
$diff = $end->getTimestamp() - $start->getTimestamp();
$hours = $diff / ( 60 * 60 );
echo $hours . ' hours (60 * 60)';

//difference between days
$diff = $end->diff($start);
$hours = $diff->h + ($diff->days * 24);
echo $hours . ' hours (days * 24)';

Ergebnis

47 hours (iterator_count)
47 hours (60 * 60)
47 hours (days * 24)

Stunden mit Sommerzeit zählen https://3v4l.org/QBQUB

Bitte beachten Sie, dass DatePeriodeine Stunde für die Sommerzeit ausgeschlossen ist, aber keine weitere Stunde hinzugefügt wird, wenn die Sommerzeit endet. Die Verwendung hängt also von Ihrem gewünschten Ergebnis und Datumsbereich ab.

Siehe den aktuellen Fehlerbericht

//set timezone to UTC to disregard daylight savings
date_default_timezone_set('America/New_York');

$interval = new \DateInterval('PT1H');

//DST starts Apr. 2nd 02:00 and moves to 03:00
$start = new \DateTime('2006-04-01T12:00:00');  
$end = new \DateTime('2006-04-02T12:00:00');

$periods = new \DatePeriod($start, $interval, $end);
$hours = iterator_count($periods);
echo $hours . ' hours';

//DST ends Oct. 29th 02:00 and moves to 01:00
$start = new \DateTime('2006-10-28T12:00:00');
$end = new \DateTime('2006-10-29T12:00:00'); 

$periods = new \DatePeriod($start, $interval, $end);
$hours = iterator_count($periods);
echo $hours . ' hours';

Ergebnis

#2006-04-01 12:00 EST to 2006-04-02 12:00 EDT
23 hours (iterator_count)
//23 hours (60 * 60)
//24 hours (days * 24)

#2006-10-28 12:00 EDT to 2006-10-29 12:00 EST
24 hours (iterator_count)
//25 hours (60 * 60)
//24 hours (days * 24)

#2006-01-01 12:00 EST to 2007-01-01 12:00 EST
8759 hours (iterator_count)
//8760 hours (60 * 60)
//8760 hours (days * 24)

//------

#2006-04-01 12:00 UTC to 2006-04-02 12:00 UTC
24 hours (iterator_count)
//24 hours (60 * 60)
//24 hours (days * 24)

#2006-10-28 12:00 UTC to 2006-10-29 12:00 UTC
24 hours (iterator_count)
//24 hours (60 * 60)
//24 hours (days * 24)

#2006-01-01 12:00 UTC to 2007-01-01 12:00 UTC
8760 hours (iterator_count)
//8760 hours (60 * 60)
//8760 hours (days * 24)
fyrye
quelle
1
Für alle, die so verwirrt waren wie ich, als sie den DateInterval-Konstruktorparameter sahen, ist das Format eine ISO 8601-Dauer
TheKarateKid
Ein weiterer Hinweis ist, dass DateIntervalkeine Bruchwerte wie in der ISO 8601-Spezifikation akzeptiert werden. Es P1.2Yist also keine gültige Dauer in PHP.
Fyrye
HINWEIS: iterator_count gibt nur positive Ergebnisse zurück. Wenn das erste Datum größer als das zweite ist, ist das Diff-Ergebnis 0.
SubjectDelta
1
@SubjectDelta, mit dem das Problem nicht zusammenhängt iterator_count, ist darauf zurückzuführen, dass DatePeriodkeine Daten generiert werden können, wenn das Startdatum ab dem Enddatum in der Zukunft liegt. Siehe: 3v4l.org/Ypsp1 Um ein negatives Datum zu verwenden, müssen Sie ein negatives Intervall angeben, DateInterval::createFromDateString('-1 hour');dessen Startdatum in der Vergangenheit vom Enddatum liegt.
Fyrye
1
@SubjectDelta Dies ist eine weitere Nuance von DatePeriod, da standardmäßig das Startdatum zwischen den angegebenen Zeiträumen enthalten ist, sofern diese nicht kleiner oder gleich dem Startdatum sind. Tatsächlich weisen Sie PHP an, innerhalb von 1 Sekunde einen Zeitraum von 1 Stunde zwischen den beiden Daten zu erstellen. Sie müssten die Minuten und Sekunden aus Ihren Datumsobjekten entfernen, da sie für die Berechnung nicht relevant sind DateTime::setTime(date->format('H'), 0). 3v4l.org/K7uss Auf diese Weise wird kein anderes Datum erstellt, wenn Sie 1 Sekunde über dem Bereich liegen.
Fyrye
17

Ihre Antwort lautet:

round((strtotime($day2) - strtotime($day1))/(60*60))

Sergey Eremin
quelle
5
Was ist, wenn zwischen 2 Stunden und 30 Minuten liegen? Ihre Antwort wird in 3 Stunden ergeben. Ich denke, es wäre besser, den Boden zu verwenden, damit sich 2 Stunden ergeben. Kommt aber wirklich auf die Situation an.
Kapitein Witbaard
14

Der einfachste Weg, um die richtige Anzahl von Stunden zwischen zwei Daten (Datums- und Uhrzeitangaben) zu ermitteln, selbst bei Änderungen der Sommerzeit, besteht darin, die Differenz der Unix-Zeitstempel zu verwenden. Unix-Zeitstempel sind Sekunden, die seit 1970-01-01T00: 00: 00 UTC verstrichen sind, wobei Schaltsekunden ignoriert werden (dies ist in Ordnung, da Sie diese Genauigkeit wahrscheinlich nicht benötigen und es ziemlich schwierig ist, Schaltsekunden zu berücksichtigen).

Die flexibelste Möglichkeit, eine Datums- / Uhrzeitzeichenfolge mit optionalen Zeitzoneninformationen in einen Unix-Zeitstempel zu konvertieren, besteht darin, ein DateTime- Objekt (optional mit einer DateTimeZone) zu erstellen als zweitem Argument im Konstruktor) zu erstellen und anschließend die Methode getTimestamp aufzurufen .

$str1 = '2006-04-12 12:30:00'; 
$str2 = '2006-04-14 11:30:00';
$tz1 = new DateTimeZone('Pacific/Apia');
$tz2 = $tz1;
$d1 = new DateTime($str1, $tz1); // tz is optional,
$d2 = new DateTime($str2, $tz2); // and ignored if str contains tz offset
$delta_h = ($d2->getTimestamp() - $d1->getTimestamp()) / 3600;
if ($rounded_result) {
   $delta_h = round ($delta_h);
} else if ($truncated_result) {
   $delta_h = intval($delta_h);
}
echo "Δh: $delta_h\n";
Walter Tross
quelle
1
Aus einem Kommentar im Handbuch geht hervor, dass aus Gründen der Kompatibilität mit Daten vor der Epocheformat("U")getTimestamp()
Arth
1
@Arth, ich weiß nicht, wann dies der Fall war, aber in meinem PHP 5.5.9 ist es nicht mehr wahr. getTimestamp()gibt jetzt genau den gleichen Wert zurück wie format("U"). Ersteres ist jedoch eine Ganzzahl, während letzteres eine Zeichenfolge ist (hier weniger effizient).
Walter Tross
Cool, vielleicht stimmte es in einer früheren Version. Ja, eine ganze Zahl wäre sauberer, also würde ich es vorziehen, getTimestamp()wenn ich sicher sein könnte.
Arth
4
//Calculate number of hours between pass and now
$dayinpass = "2013-06-23 05:09:12";
$today = time();
$dayinpass= strtotime($dayinpass);
echo round(abs($today-$dayinpass)/60/60);
Hoàng Vũ Tgtt
quelle
3
$day1 = "2006-04-12 12:30:00"
$day1 = strtotime($day1);
$day2 = "2006-04-14 11:30:00"
$day2 = strtotime($day2);

$diffHours = round(($day2 - $day1) / 3600);

Ich denke, die Funktion strtotime () akzeptiert dieses Datumsformat.

Boris Delormas
quelle
3
<?
     $day1 = "2014-01-26 11:30:00";
     $day1 = strtotime($day1);
     $day2 = "2014-01-26 12:30:00";
     $day2 = strtotime($day2);

   $diffHours = round(($day2 - $day1) / 3600);

   echo $diffHours;

?>
Nachtwanderer
quelle
Dies ist auch eine Kopie des Antwortformulars 2010.
Daniel W.
2

Leider funktioniert die von FaileN bereitgestellte Lösung nicht wie von Walter Tross angegeben. Tage können nicht 24 Stunden sein!

Ich benutze die PHP-Objekte gerne, wo es möglich ist, und für ein bisschen mehr Flexibilität habe ich mir die folgende Funktion ausgedacht:

/**
 * @param DateTimeInterface $a
 * @param DateTimeInterface $b
 * @param bool              $absolute Should the interval be forced to be positive?
 * @param string            $cap The greatest time unit to allow
 *
 * @return DateInterval The difference as a time only interval
 */
function time_diff(DateTimeInterface $a, DateTimeInterface $b, $absolute=false, $cap='H'){

  // Get unix timestamps, note getTimeStamp() is limited
  $b_raw = intval($b->format("U"));
  $a_raw = intval($a->format("U"));

  // Initial Interval properties
  $h = 0;
  $m = 0;
  $invert = 0;

  // Is interval negative?
  if(!$absolute && $b_raw<$a_raw){
    $invert = 1;
  }

  // Working diff, reduced as larger time units are calculated
  $working = abs($b_raw-$a_raw);

  // If capped at hours, calc and remove hours, cap at minutes
  if($cap == 'H') {
    $h = intval($working/3600);
    $working -= $h * 3600;
    $cap = 'M';
  }

  // If capped at minutes, calc and remove minutes
  if($cap == 'M') {
    $m = intval($working/60);
    $working -= $m * 60;
  }

  // Seconds remain
  $s = $working;

  // Build interval and invert if necessary
  $interval = new DateInterval('PT'.$h.'H'.$m.'M'.$s.'S');
  $interval->invert=$invert;

  return $interval;
}

Dies date_diff()schafft eine DateTimeInterval, aber mit der höchsten Einheit als Stunden statt Jahre .. es kann wie gewohnt formatiert werden.

$interval = time_diff($date_a, $date_b);
echo $interval->format('%r%H'); // For hours (with sign)

NB Ich habe format('U')statt getTimestamp()wegen des Kommentars im Handbuch verwendet . Beachten Sie auch, dass 64-Bit für Daten nach und nach der Epoche erforderlich ist!

Arth
quelle
0

Mit dieser Funktion können Sie genaue Jahre und Monate zwischen zwei angegebenen Daten $doj1und berechnen $doj. Beispiel 4.3 bedeutet 4 Jahre und 3 Monate.

<?php
    function cal_exp($doj1)
    {
        $doj1=strtotime($doj1);
        $doj=date("m/d/Y",$doj1); //till date or any given date

        $now=date("m/d/Y");
        //$b=strtotime($b1);
        //echo $c=$b1-$a2;
        //echo date("Y-m-d H:i:s",$c);
        $year=date("Y");
        //$chk_leap=is_leapyear($year);

        //$year_diff=365.25;

        $x=explode("/",$doj);
        $y1=explode("/",$now);

        $yy=$x[2];
        $mm=$x[0];
        $dd=$x[1];

        $yy1=$y1[2];
        $mm1=$y1[0];
        $dd1=$y1[1];
        $mn=0;
        $mn1=0;
        $ye=0;
        if($mm1>$mm)
        {
            $mn=$mm1-$mm;
            if($dd1<$dd)
            {
                $mn=$mn-1;
            }
            $ye=$yy1-$yy;
        }
        else if($mm1<$mm)
        {
            $mn=12-$mm;
            //$mn=$mn;

            if($mm!=1)
            {
                $mn1=$mm1-1;
            }

            $mn+=$mn1;
            if($dd1>$dd)
            {
                $mn+=1;
            }

            $yy=$yy+1;
            $ye=$yy1-$yy;
        }
        else
        {
            $ye=$yy1-$yy;
            $ye=$ye-1;

            $mn=12-1;

            if($dd1>$dd)
            {
                $ye+=1;
                $mn=0;
            }
        }

        $to=$ye." year and ".$mn." months";
        return $ye.".".$mn;

        /*return daysDiff($x[2],$x[0],$x[1]);
         $days=dateDiff("/",$now,$doj)/$year_diff;
        $days_exp=explode(".",$days);
        return $years_exp=$days; //number of years exp*/
    }
?>
Geomagas
quelle
Die vorgeschlagene Bearbeitung ist zu geringfügig, <phpmuss jedoch geändert werden in <?phpoder die vorgeschlagene Änderung genehmigen, wodurch der Fehler insgesamt behoben wird.
Anishsane
0

Das funktioniert in meinem Projekt. Ich denke, das wird hilfreich für dich sein.

Wenn das Datum in der Vergangenheit liegt, wird invertiert 1.
Wenn das Datum in der Zukunft liegt, wird invertiert 0.

$defaultDate = date('Y-m-d');   
$datetime1   = new DateTime('2013-03-10');  
$datetime2   = new DateTime($defaultDate);  
$interval    = $datetime1->diff($datetime2);  
$days        = $interval->format('%a');
$invert      = $interval->invert;
Gurudutt Sharma
quelle
0

Verwenden Sie diese Notation, um einen Unix-Zeitstempel zu übergeben

$now        = time();
$now        = new DateTime("@$now");
Steve Whitby
quelle
1
Hinweis Die Zeitzone wird wie +0:00bei der Verwendung @im DateTime-Konstruktor übergeben und ausgegeben . Während der Verwendung der DateTime::modify()Methode wird der Zeitstempel als übergeben +0:00und die aktuelle Zeitzone ausgegeben. Alternativ verwenden Sie $date = new DateTime(); $date->setTimestamp($unix_timestamp);siehe: 3v4l.org/BoAWI
fyrye
0

Carbon könnte auch ein guter Weg sein.

Von ihrer Website:

Eine einfache PHP-API-Erweiterung für DateTime. http://carbon.nesbot.com/

Beispiel:

use Carbon\Carbon;

//...

$day1 = Carbon::createFromFormat('Y-m-d H:i:s', '2006-04-12 12:30:00');
$day2 = Carbon::createFromFormat('Y-m-d H:i:s', '2006-04-14 11:30:00');

echo $day1->diffInHours($day2); // 47

//...

Carbon erweitert die DateTime-Klasse, um Methoden zu erben, einschließlich diff(). Es fügt schönen Zucker wie diffInHours, diffInMintutes, diffInSecondsusw.

Joshuamabina
quelle
0

Zunächst sollten Sie ein Intervallobjekt aus einem Datumsbereich erstellen. Allein anhand des in diesem Satz verwendeten Wortlauts können die erforderlichen grundlegenden Abstraktionen leicht identifiziert werden. Es gibt ein Intervall als Konzept, und einige weitere Möglichkeiten, es umzusetzen, umfassen das bereits erwähnte - aus einer Reihe von Daten. Ein Intervall sieht also so aus:

$interval =
    new FromRange(
        new FromISO8601('2017-02-14T14:27:39+00:00'),
        new FromISO8601('2017-03-14T14:27:39+00:00')
    );

FromISO8601 hat die gleiche Semantik: Es ist ein datetime-Objekt erstellt from iso8601-formatted string , daher der Name.

Wenn Sie ein Intervall haben, können Sie es nach Belieben formatieren. Wenn Sie mehrere volle Stunden benötigen, können Sie haben

(new TotalFullHours($interval))->value();

Wenn Sie eine begrenzte Gesamtstundenzahl wünschen, können Sie Folgendes tun:

(new TotalCeiledHours($interval))->value();

Weitere Informationen zu diesem Ansatz und einige Beispiele finden Sie in diesem Eintrag .

Vadim Samokhin
quelle
0

Zusätzlich zu der sehr hilfreichen Antwort von @ fyrye ist dies eine gute Lösung für den erwähnten Fehler ( dieser) ), bei dem DatePeriod beim Eintritt in die Sommerzeit eine Stunde abzieht, beim Verlassen der Sommerzeit jedoch keine Stunde hinzufügt (und somit der März in Europa / Berlin seinen hat korrekte 743 Stunden, aber der Oktober hat 744 statt 745 Stunden):

Zählen der Stunden eines Monats (oder einer beliebigen Zeitspanne) unter Berücksichtigung der Sommerzeitübergänge in beide Richtungen

function getMonthHours(string $year, string $month, \DateTimeZone $timezone): int
{
    // or whatever start and end \DateTimeInterface objects you like
    $start = new \DateTimeImmutable($year . '-' . $month . '-01 00:00:00', $timezone);
    $end = new \DateTimeImmutable((new \DateTimeImmutable($year . '-' . $month . '-01 23:59:59', $timezone))->format('Y-m-t H:i:s'), $timezone);
    
    // count the hours just utilizing \DatePeriod, \DateInterval and iterator_count, hell yeah!
    $hours = iterator_count(new \DatePeriod($start, new \DateInterval('PT1H'), $end));
    
    // find transitions and check, if there is one that leads to a positive offset
    // that isn't added by \DatePeriod
    // this is the workaround for https://bugs.php.net/bug.php?id=75685
    $transitions = $timezone->getTransitions((int)$start->format('U'), (int)$end->format('U'));
    if (2 === count($transitions) && $transitions[0]['offset'] - $transitions[1]['offset'] > 0) {
        $hours += (round(($transitions[0]['offset'] - $transitions[1]['offset'])/3600));
    }
    
    return $hours;
}

$myTimezoneWithDST = new \DateTimeZone('Europe/Berlin');
var_dump(getMonthHours('2020', '01', $myTimezoneWithDST)); // 744
var_dump(getMonthHours('2020', '03', $myTimezoneWithDST)); // 743
var_dump(getMonthHours('2020', '10', $myTimezoneWithDST)); // 745, finally!

$myTimezoneWithoutDST = new \DateTimeZone('UTC');
var_dump(getMonthHours('2020', '01', $myTimezoneWithoutDST)); // 744
var_dump(getMonthHours('2020', '03', $myTimezoneWithoutDST)); // 744
var_dump(getMonthHours('2020', '10', $myTimezoneWithoutDST)); // 744

PS Wenn Sie eine (längere) Zeitspanne überprüfen, die zu mehr als diesen beiden Übergängen führt, berührt meine Problemumgehung die gezählten Stunden nicht, um das Potenzial für lustige Nebenwirkungen zu verringern. In solchen Fällen muss eine kompliziertere Lösung implementiert werden. Man könnte alle gefundenen Übergänge durchlaufen und den Strom mit dem letzten vergleichen und prüfen, ob es einer mit DST true-> false ist.

Spackmat
quelle