Calculer le nombre d'heures entre 2 dates en PHP


107

Comment calculer la différence entre deux dates en heures?

Par exemple:

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

Dans ce cas, le résultat devrait être de 47 heures.


1
Ma réponse initiale aurait été de transformer les deux valeurs en horodatages en utilisant strftime()et de diviser la différence par 3600, mais cela fonctionnera-t-il toujours? Damn you, heure d'été!
Pekka

@Pekka: non ça ne marchera pas toujours je suppose ... Jetez un œil à ma réponse. Là, j'ai posté une solution en tenant compte des fuseaux horaires, des années bissextiles, des secondes bissextiles et dst :)
Fidi

@Pekka, si vous l'utilisez strtotime()fonctionnera toujours, tant que vous utilisez le fuseau horaire par défaut OU spécifiez explicitement le décalage du fuseau horaire. Aucune raison de maudire le DST.
Walter Tross

Réponses:


205

Les versions PHP plus récentes fournissent de nouvelles classes appelées DateTime, DateInterval, DateTimeZoneet DatePeriod. Ce qui est cool à propos de ces cours, c'est qu'ils prennent en compte différents fuseaux horaires, années bissextiles, secondes intercalaires, heure d'été, etc. Et en plus de cela, il est très facile à utiliser. Voici ce que vous voulez avec l'aide de ces objets:

// 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');

L'objet DateInterval, qui est retourné, fournit également d'autres méthodes que format. Si vous voulez le résultat en heures seulement, vous pouvez faire quelque chose comme ceci:

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

Et voici les liens vers la documentation:

Toutes ces classes offrent également une manière procédurale / fonctionnelle d'opérer avec des dates. Jetez donc un œil à l'aperçu: http://php.net/manual/book.datetime.php


+1 Bon travail! Cela semble solide et constitue un bon aperçu. Il est important de noter que les calculs peuvent varier en fonction du fuseau horaire en raison de différentes règles d'heure d'été, c'est donc probablement une bonne idée de toujours définir la zone et de ne pas se fier aux paramètres du serveur.
Pekka

Oui. Avec cet objet, vous pouvez même calculer entre des dates dans différents fuseaux horaires. $date1 = new DateTime('2006-04-12T12:30:00 Europe/Berlin');et$date2 = new DateTime('2006-04-14T11:30:00 America/New_York');
Fidi

3
Si quelqu'un rencontre le même problème que moi, où $diff->dest égal à 0 (parce que j'essaie de calculer les heures entre deux dates qui sont exactement à 2 mois d'intervalle): Running var_dump($diff)m'a montré un autre paramètre:, ["days"]=>int(61)alors j'ai fini par utiliser $hours = $diff->days * 24;, et il est venu proche de la "moyenne" de 1440 heures pour 2 mois de 30 jours, donc cela semble beaucoup mieux qu'un résultat de 0. (Je suppose que ma version PHP est un peu ancienne ...)
semmelbroesel

2
Je veux dire, dans de nombreuses régions du monde, une année compte une journée de 23 heures et une journée de 25 heures.
Walter Tross

4
@Amal Murali, vous avez donc décidé d'attribuer le bonus à cette réponse, qui est FAUX? Avez-vous essayé de calculer avec cette réponse le nombre d'heures entre midi du premier janvier et midi du premier juin, dans n'importe quel fuseau horaire qui a DST (heure d'été)? Vous obtiendrez un résultat pair, tandis que le vrai résultat est étrange.
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 );

4
Pourquoi pas $diff / 3600?
Alex G du

4
@AlexG C'est juste une question de style. Même résultat, mais les programmeurs utilisent généralement la multiplication quand il s'agit de temps
Utilisateur

je vous suggère d'aimer: round (($ t1 - $ 22) / 3600); utilisez le tour pour obtenir les heures correctes
Shiv Singh

20

Pour fournir une autre méthode DatePeriodlors de l'utilisation du fuseau horaire UTC ou GMT .

Compter les heures 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)';

Résultat

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

Compter les heures avec l'heure d'été https://3v4l.org/QBQUB

Veuillez noter que cela DatePeriodexclut une heure pour l'heure d'été mais n'ajoute pas une autre heure lorsque l'heure d'été se termine. Son utilisation est donc subjective par rapport au résultat souhaité et à la plage de dates.

Voir le rapport de bogue actuel

//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';

Résultat

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

1
Pour toute personne aussi confuse que moi en voyant le paramètre du constructeur DateInterval, le format est une durée ISO 8601
TheKarateKid

Une autre note est que DateIntervaln'accepte pas les valeurs fractionnaires comme dans la spécification ISO 8601. Ce P1.2Yn'est donc pas une durée valide en PHP.
fyrye

REMARQUE: iterator_count renverra uniquement des résultats positifs. Si la première date est plus grande que la seconde, le résultat de la différence sera 0.
SubjectDelta

1
@SubjectDelta le problème n'est pas lié iterator_count, il est dû à l' DatePeriodimpossibilité de générer des dates lorsque la date de début est dans le futur à partir de la date de fin. Voir: 3v4l.org/Ypsp1 pour utiliser une date négative, vous devez spécifier un intervalle négatif, DateInterval::createFromDateString('-1 hour');avec une date de début dans le passé de à partir de la date de fin.
fyrye

1
@SubjectDelta c'est une autre nuance de DatePeriod, car par défaut, il inclura la date de début entre les périodes spécifiées à moins qu'elles ne soient inférieures ou égales à la date de début. En effet, vous dites à php de créer une période d'une heure entre les deux dates, dans un délai d'une seconde. Vous devrez supprimer les minutes et les secondes de vos objets de date, car ils ne sont pas pertinents dans le calcul, en utilisant DateTime::setTime(date->format('H'), 0). 3v4l.org/K7uss De cette façon, si vous dépassez la plage de 1 seconde, une autre date n'est pas créée.
fyrye

17

votre réponse est:

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


5
Et s'il y a 2 heures et 30 minutes entre les deux? Votre réponse se traduira par 3 heures. Je pense qu'il serait préférable d'utiliser le sol pour que cela donne 2 heures. Cela dépend vraiment de la situation.
Kapitein Witbaard

14

Le moyen le plus simple d'obtenir le nombre d'heures correct entre deux dates (datetimes), même en cas de changement d'heure d'été, est d'utiliser la différence d'horodatage Unix. Les horodatages Unix sont des secondes écoulées depuis 1970-01-01T00: 00: 00 UTC, ignorant les secondes intercalaires (c'est OK parce que vous n'avez probablement pas besoin de cette précision, et parce qu'il est assez difficile de prendre en compte les secondes intercalaires).

Le moyen le plus flexible de convertir une chaîne datetime avec des informations de fuseau horaire facultatives en un horodatage Unix est de construire un objet DateTime (éventuellement avec un DateTimeZone comme second argument dans le constructeur), puis d'appeler sa méthode getTimestamp .

$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";

1
D'après un commentaire dans le manuel, il semble que, pour la compatibilité avec les dates antérieures à l'époque, il format("U")est préférable degetTimestamp()
Arth

1
@Arth, je ne sais pas quand c'était le cas, mais dans mon PHP 5.5.9 ce n'est plus vrai. getTimestamp()renvoie maintenant exactement la même valeur que format("U"). Le premier est un entier, tandis que le second est une chaîne (moins efficace ici).
Walter Tross

Cool, c'était peut-être vrai dans une version antérieure. Oui, un entier serait plus propre, donc je préférerais getTimestamp()que je puisse être sûr.
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);

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

Je suppose que la fonction strtotime () accepte ce format de date.


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;

?>

Ceci est également une copie du formulaire de réponse 2010.
Daniel W.

2

Malheureusement, la solution fournie par FaileN ne fonctionne pas comme indiqué par Walter Tross. Les jours peuvent ne pas être 24 heures!

J'aime utiliser les objets PHP dans la mesure du possible et pour un peu plus de flexibilité, j'ai proposé la fonction suivante:

/**
 * @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;
}

Cela date_diff()crée un DateTimeInterval, mais avec l'unité la plus élevée en heures plutôt qu'en années .. il peut être formaté comme d'habitude.

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

NB je l'ai utilisé format('U')au lieu de à getTimestamp()cause du commentaire dans le manuel . Notez également que 64 bits est requis pour les dates post-époque et pré-période négative!


0

Cette fonction vous aide à calculer les années et les mois exacts entre deux dates données, $doj1et $doj. Il renvoie l'exemple 4.3 signifie 4 ans et 3 mois.

<?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*/
    }
?>

La modification suggérée est trop mineure, mais <phpdoit être remplacée par <?phpOu approuver la modification suggérée, ce qui supprime le bogue dans son ensemble.
anishsane

0

Cela fonctionne dans mon projet. Je pense que cela vous sera utile.

Si la date est dans le passé, alors inverser sera 1.
Si la date est dans le futur, alors inverser sera 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;

0

Pour transmettre un horodatage unix, utilisez cette notation

$now        = time();
$now        = new DateTime("@$now");

1
Remarque Le fuseau horaire sera passé et affiché comme +0:00lors de son utilisation @dans le constructeur DateTime. Lors de l'utilisation, la DateTime::modify()méthode transmettra l'horodatage +0:00et affichera le fuseau horaire actuel. Vous pouvez également utiliser $date = new DateTime(); $date->setTimestamp($unix_timestamp);voir: 3v4l.org/BoAWI
fyrye

0

Le carbone pourrait également être une bonne solution.

Depuis leur site Web:

Une simple extension d'API PHP pour DateTime. http://carbon.nesbot.com/

Exemple:

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 étend la classe DateTime pour hériter des méthodes, y compris diff(). Il ajoute des sucres comme beaux diffInHours, diffInMintutes, diffInSecondsetc.


0

Tout d'abord, vous devez créer un objet intervalle à partir d'une plage de dates. Par le libellé utilisé dans cette phrase seulement, on peut facilement identifier les abstractions de base nécessaires. Il existe un intervalle en tant que concept, et quelques autres façons de le mettre en œuvre, incluent celui déjà mentionné - à partir d'une plage de dates. Ainsi, un intervalle ressemble à ça:

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

FromISO8601a la même sémantique: c'est un objet datetime créé from iso8601-formatted string, d'où le nom.

Lorsque vous avez un intervalle, vous pouvez le formater comme vous le souhaitez. Si vous avez besoin d'un certain nombre d'heures complètes, vous pouvez avoir

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

Si vous voulez un nombre total d'heures plafonné, allez-y:

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

Pour plus d'informations sur cette approche et quelques exemples, consultez cette entrée .


0

En plus de la réponse très utile de @ fyrye, il s'agit d'une solution de contournement correcte pour le bogue mentionné ( celui-ci ), que DatePeriod soustrait une heure lors de l'entrée en été, mais n'ajoute pas une heure en quittant l'heure d'été (et donc la marche d'Europe / Berlin a son correct 743 heures mais octobre a 744 heures au lieu de 745 heures):

Compter les heures d'un mois (ou n'importe quel intervalle de temps), en tenant compte des transitions DST dans les deux sens

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 Si vous cochez une période (plus longue), ce qui conduit à plus que ces deux transitions, ma solution de contournement ne touchera pas les heures comptées pour réduire le potentiel d'effets secondaires amusants. Dans de tels cas, une solution plus compliquée doit être mise en œuvre. On pourrait parcourir toutes les transitions trouvées et comparer le courant avec le dernier et vérifier s'il en est un avec DST true-> false.


0
$diff_min = ( strtotime( $day2 ) - strtotime( $day1 ) ) / 60 / 60;
$total_time  = $diff_min;

Vous pouvez essayer celui-ci.

En utilisant notre site, vous reconnaissez avoir lu et compris notre politique liée aux cookies et notre politique de confidentialité.
Licensed under cc by-sa 3.0 with attribution required.