822 votes

Comment calculer la différence entre deux dates en utilisant PHP ?

J'ai deux dates du formulaire :

Start Date: 2007-03-24 
End Date: 2009-06-26

Je dois maintenant trouver la différence entre les deux sous la forme suivante :

2 years, 3 months and 2 days

Comment puis-je faire cela en PHP ?

3 votes

2 ans 94 jours. Le calcul des mois, en tenant compte des années bissextiles, serait problématique. Quelle doit être la précision de ce calcul ?

0 votes

Duplication possible de Comment calculer le temps relatif ?

1038voto

jurka Points 5050

Je suggère d'utiliser les objets DateTime et DateInterval.

$date1 = new DateTime("2007-03-24");
$date2 = new DateTime("2009-06-26");
$interval = $date1->diff($date2);
echo "difference " . $interval->y . " years, " . $interval->m." months, ".$interval->d." days "; 

en savoir plus php DateTime::diff manuel

EDIT (en date de juin 2012) :

// shows the total amount of days (not divided into years, months and days like above)
echo "difference " . $interval->days . " days ";

19 votes

+1 DateTime gère correctement les années bissextiles et les fuseaux horaires et voilà un bon livre pour l'étagère : phparch.com/books/

3 votes

Existe-t-il une méthode qui donne le nombre total de secondes entre les deux DateTime ? (sans additionner les composants)

0 votes

Corrigez-moi si je me trompe, mais je pense qu'il faut le faire. $interval->days au lieu de $interval->d ! ?!?!?

593voto

Emil H Points 24062

Vous pouvez utiliser strtotime() pour convertir deux dates en heure unix et ensuite calculer le nombre de secondes qui les séparent. À partir de là, il est assez facile de calculer différentes périodes de temps.

$date1 = "2007-03-24";
$date2 = "2009-06-26";

$diff = abs(strtotime($date2) - strtotime($date1));

$years = floor($diff / (365*60*60*24));
$months = floor(($diff - $years * 365*60*60*24) / (30*60*60*24));
$days = floor(($diff - $years * 365*60*60*24 - $months*30*60*60*24)/ (60*60*24));

printf("%d years, %d months, %d days\n", $years, $months, $days);

Edit : Évidemment, la meilleure façon de procéder est celle décrite par jurka ci-dessous. Mon code n'est généralement recommandé que si vous n'avez pas PHP 5.3 ou mieux.

Plusieurs personnes dans les commentaires ont fait remarquer que le code ci-dessus n'est qu'une approximation. Je continue à penser que pour la plupart des cas, c'est très bien, puisque l'utilisation d'une plage est plus destinée à donner une idée du temps qui s'est écoulé ou qui reste plutôt qu'à fournir une précision - si vous voulez faire cela, il suffit d'afficher la date.

Malgré tout cela, j'ai décidé de répondre aux plaintes. Si vous avez vraiment besoin d'un intervalle exact mais que vous n'avez pas accès à PHP 5.3, utilisez le code ci-dessous (il devrait également fonctionner en PHP 4). C'est un portage direct du code que PHP utilise en interne pour calculer les intervalles, à l'exception qu'il ne prend pas en compte l'heure d'été. Cela signifie qu'il est décalé d'une heure au maximum, mais à part cela, il devrait être correct.

<?php

/**
 * Calculate differences between two dates with precise semantics. Based on PHPs DateTime::diff()
 * implementation by Derick Rethans. Ported to PHP by Emil H, 2011-05-02. No rights reserved.
 * 
 * See here for original code:
 * http://svn.php.net/viewvc/php/php-src/trunk/ext/date/lib/tm2unixtime.c?revision=302890&view=markup
 * http://svn.php.net/viewvc/php/php-src/trunk/ext/date/lib/interval.c?revision=298973&view=markup
 */

function _date_range_limit($start, $end, $adj, $a, $b, $result)
{
    if ($result[$a] < $start) {
        $result[$b] -= intval(($start - $result[$a] - 1) / $adj) + 1;
        $result[$a] += $adj * intval(($start - $result[$a] - 1) / $adj + 1);
    }

    if ($result[$a] >= $end) {
        $result[$b] += intval($result[$a] / $adj);
        $result[$a] -= $adj * intval($result[$a] / $adj);
    }

    return $result;
}

function _date_range_limit_days($base, $result)
{
    $days_in_month_leap = array(31, 31, 29, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31);
    $days_in_month = array(31, 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31);

    _date_range_limit(1, 13, 12, "m", "y", &$base);

    $year = $base["y"];
    $month = $base["m"];

    if (!$result["invert"]) {
        while ($result["d"] < 0) {
            $month--;
            if ($month < 1) {
                $month += 12;
                $year--;
            }

            $leapyear = $year % 400 == 0 || ($year % 100 != 0 && $year % 4 == 0);
            $days = $leapyear ? $days_in_month_leap[$month] : $days_in_month[$month];

            $result["d"] += $days;
            $result["m"]--;
        }
    } else {
        while ($result["d"] < 0) {
            $leapyear = $year % 400 == 0 || ($year % 100 != 0 && $year % 4 == 0);
            $days = $leapyear ? $days_in_month_leap[$month] : $days_in_month[$month];

            $result["d"] += $days;
            $result["m"]--;

            $month++;
            if ($month > 12) {
                $month -= 12;
                $year++;
            }
        }
    }

    return $result;
}

function _date_normalize($base, $result)
{
    $result = _date_range_limit(0, 60, 60, "s", "i", $result);
    $result = _date_range_limit(0, 60, 60, "i", "h", $result);
    $result = _date_range_limit(0, 24, 24, "h", "d", $result);
    $result = _date_range_limit(0, 12, 12, "m", "y", $result);

    $result = _date_range_limit_days(&$base, &$result);

    $result = _date_range_limit(0, 12, 12, "m", "y", $result);

    return $result;
}

/**
 * Accepts two unix timestamps.
 */
function _date_diff($one, $two)
{
    $invert = false;
    if ($one > $two) {
        list($one, $two) = array($two, $one);
        $invert = true;
    }

    $key = array("y", "m", "d", "h", "i", "s");
    $a = array_combine($key, array_map("intval", explode(" ", date("Y m d H i s", $one))));
    $b = array_combine($key, array_map("intval", explode(" ", date("Y m d H i s", $two))));

    $result = array();
    $result["y"] = $b["y"] - $a["y"];
    $result["m"] = $b["m"] - $a["m"];
    $result["d"] = $b["d"] - $a["d"];
    $result["h"] = $b["h"] - $a["h"];
    $result["i"] = $b["i"] - $a["i"];
    $result["s"] = $b["s"] - $a["s"];
    $result["invert"] = $invert ? 1 : 0;
    $result["days"] = intval(abs(($one - $two)/86400));

    if ($invert) {
        _date_normalize(&$a, &$result);
    } else {
        _date_normalize(&$b, &$result);
    }

    return $result;
}

$date = "1986-11-10 19:37:22";

print_r(_date_diff(strtotime($date), time()));
print_r(_date_diff(time(), strtotime($date)));

1 votes

Si vous utilisez la classe DateTime, vous pouvez utiliser $date->format('U') pour obtenir l'horodatage unix.

4 votes

Ce n'est pas vrai si l'on est confronté à l'heure d'été ou à l'heure d'hiver. Dans ce cas particulier, lorsque l'on ajuste l'heure d'été et l'heure d'hiver, un jour équivaut à 23 ou 25 heures.

4 votes

Le même argument pourrait être avancé pour les années bissextiles. Il n'en tient pas compte non plus. Pourtant, je ne suis pas convaincu que vous souhaitiez même en tenir compte puisque nous discutons ici d'une fourchette. La sémantique d'une fourchette est quelque peu différente de celle d'une date absolue.

88voto

Second Rikudo Points 59550

La meilleure solution est d'utiliser la fonction de PHP DateTime (et DateInterval ). Chaque date est encapsulée dans un objet DateTime et on peut alors faire la différence entre les deux :

$first_date = new DateTime("2012-11-30 17:03:30");
$second_date = new DateTime("2012-12-21 00:00:00");

Le site DateTime acceptera tout format strtotime() serait. Si un format de date encore plus spécifique est nécessaire, DateTime::createFromFormat() peut être utilisé pour créer le DateTime objet.

Après que les deux objets aient été instanciés, vous soustrayez l'un de l'autre avec DateTime::diff() .

$difference = $first_date->diff($second_date);

$difference détient maintenant un DateInterval avec les informations sur les différences. A var_dump() ressemble à ça :

object(DateInterval)
  public 'y' => int 0
  public 'm' => int 0
  public 'd' => int 20
  public 'h' => int 6
  public 'i' => int 56
  public 's' => int 30
  public 'invert' => int 0
  public 'days' => int 20

Pour formater le DateInterval nous devrons vérifier chaque valeur et l'exclure si elle est égale à 0 :

/**
 * Format an interval to show all existing components.
 * If the interval doesn't have a time component (years, months, etc)
 * That component won't be displayed.
 *
 * @param DateInterval $interval The interval
 *
 * @return string Formatted interval string.
 */
function format_interval(DateInterval $interval) {
    $result = "";
    if ($interval->y) { $result .= $interval->format("%y years "); }
    if ($interval->m) { $result .= $interval->format("%m months "); }
    if ($interval->d) { $result .= $interval->format("%d days "); }
    if ($interval->h) { $result .= $interval->format("%h hours "); }
    if ($interval->i) { $result .= $interval->format("%i minutes "); }
    if ($interval->s) { $result .= $interval->format("%s seconds "); }

    return $result;
}

Tout ce qui reste à faire maintenant est d'appeler notre fonction sur les $difference DateInterval objet :

echo format_interval($difference);

Et nous obtenons le bon résultat :

20 jours 6 heures 56 minutes 30 secondes

Le code complet utilisé pour atteindre l'objectif :

/**
 * Format an interval to show all existing components.
 * If the interval doesn't have a time component (years, months, etc)
 * That component won't be displayed.
 *
 * @param DateInterval $interval The interval
 *
 * @return string Formatted interval string.
 */
function format_interval(DateInterval $interval) {
    $result = "";
    if ($interval->y) { $result .= $interval->format("%y years "); }
    if ($interval->m) { $result .= $interval->format("%m months "); }
    if ($interval->d) { $result .= $interval->format("%d days "); }
    if ($interval->h) { $result .= $interval->format("%h hours "); }
    if ($interval->i) { $result .= $interval->format("%i minutes "); }
    if ($interval->s) { $result .= $interval->format("%s seconds "); }

    return $result;
}

$first_date = new DateTime("2012-11-30 17:03:30");
$second_date = new DateTime("2012-12-21 00:00:00");

$difference = $first_date->diff($second_date);

echo format_interval($difference);

0 votes

DateTime() n'est pas une fonction, c'est un objet, et il existe depuis PHP 5.2. Assurez-vous que votre serveur le supporte.

2 votes

@SecondRikudo DateTime::Diff nécessite PHP 5.3.0

0 votes

Nous avons un problème, nous échangeons first_date avec second_date et nous obtenons le même résultat ? Pourquoi ne pas dire 0 jours 0 heures 0 minutes 0 secondes ou seulement 0. Exemple : 2012-11-30 17:03:30 - 2012-12-21 00:00:00 et 2012-12-21 00:00:00 - 2012-11-30 17:03:30 donnent le même résultat.

40voto

khaldonno Points 263

Voir les heures, les minutes et les secondes

$date1 = "2008-11-01 22:45:00"; 

$date2 = "2009-12-04 13:44:01"; 

$diff = abs(strtotime($date2) - strtotime($date1)); 

$years   = floor($diff / (365*60*60*24)); 
$months  = floor(($diff - $years * 365*60*60*24) / (30*60*60*24)); 
$days    = floor(($diff - $years * 365*60*60*24 - $months*30*60*60*24)/ (60*60*24));

$hours   = floor(($diff - $years * 365*60*60*24 - $months*30*60*60*24 - $days*60*60*24)/ (60*60)); 

$minuts  = floor(($diff - $years * 365*60*60*24 - $months*30*60*60*24 - $days*60*60*24 - $hours*60*60)/ 60); 

$seconds = floor(($diff - $years * 365*60*60*24 - $months*30*60*60*24 - $days*60*60*24 - $hours*60*60 - $minuts*60)); 

printf("%d years, %d months, %d days, %d hours, %d minuts\n, %d seconds\n", $years, $months, $days, $hours, $minuts, $seconds);

9 votes

Il est probable que cela ne donne pas un résultat exact.

8 votes

Et c'est une solution terrible à moins que vous ne soyez obligé d'utiliser une version terriblement obsolète de PHP ...

3 votes

Ce n'est pas le cas SEC . Par exemple, 60*60*24 est répété 15 fois. Vive la réutilisation du copier-coller !

12voto

Jakobud Points 14581

Je ne sais pas si vous utilisez un framework PHP ou non, mais beaucoup de frameworks PHP ont des bibliothèques de date/heure et des aides pour vous éviter de réinventer la roue.

Par exemple, CodeIgniter dispose de l'option timespan() fonction. Il suffit d'entrer deux timestamps Unix et elle génère automatiquement un résultat comme celui-ci :

1 Year, 10 Months, 2 Weeks, 5 Days, 10 Hours, 16 Minutes

http://codeigniter.com/user_guide/helpers/date_helper.html

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X