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

722

J'ai deux dates du formulaire:

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

Maintenant, je dois trouver la différence entre ces deux sous la forme suivante:

2 years, 3 months and 2 days

Comment puis-je faire cela en PHP?

Taryn
la source
1
2 ans 94 jours. Le calcul des mois, en tenant compte des années bissextiles, serait problématique. À quel point cela doit-il être précis?
dbasnett
doublon possible de Comment calculer le temps relatif?
Cole Johnson

Réponses:

527

Utilisez-le pour le code hérité (PHP <5.3). Pour une solution à jour, voir la réponse de jurka ci-dessous

Vous pouvez utiliser strtotime () pour convertir deux dates en heure unix, puis calculer le nombre de secondes entre elles. À partir de là, il est assez facile de calculer différentes périodes.

$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: De toute évidence, la façon préférée de le faire est comme décrit 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 souligné que le code ci-dessus n'est qu'une approximation. Je pense toujours que, dans la plupart des cas, c'est très bien, car l'utilisation d'une plage est plus pour donner une idée du temps qui s'est écoulé ou reste plutôt que pour fournir une précision - si vous voulez le faire, il suffit de sortir la date.

Malgré tout cela, j'ai décidé de répondre aux plaintes. Si vous avez vraiment besoin d'une plage exacte mais que vous n'avez pas accès à PHP 5.3, utilisez le code ci-dessous (il devrait également fonctionner en PHP 4). Il s'agit d'un port direct du code que PHP utilise en interne pour calculer les plages, à l'exception qu'il ne prend pas en compte l'heure d'été. Cela signifie qu'il est éteint d'une heure au plus, 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)));
Emil H
la source
1
Si vous utilisez la classe DateTime, vous pouvez opter pour $ date-> format ('U') pour obtenir l'horodatage unix.
Jon Cram
4
Ce n'est pas vrai si vous devez composer avec l'heure d'été / d'hiver. Dans ce cas particulier, lorsque vous ajustez l'heure d'été / d'hiver, un jour équivaut à 23 ou 25 heures.
Arno
4
Eh bien, 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 car nous discutons ici d'une gamme. La sémantique d'une plage est quelque peu différente de celle d'une date absolue.
Emil H
9
Cette fonction est incorrecte. C'est bon pour une approximation, mais incorrect pour des plages exactes. D'une part, il suppose qu'il y a 30 jours dans un mois, c'est-à-dire qu'il aura la même différence de jours entre le 1er février et le 1er mars que pour le 1er juillet au 1er août (quelle que soit l'année bissextile).
enobrev
1
En PHP, les variables de référence sont dans la signature de la fonction, pas dans l'appel. Déplacez tous vos &signatures.
Paul Tarjan
909

Je suggère d'utiliser des 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 "; 

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

en savoir plus php DateTime :: diff manual

Du manuel:

Depuis PHP 5.2.2, les objets DateTime peuvent être comparés à l'aide d'opérateurs de comparaison.

$date1 = new DateTime("now");
$date2 = new DateTime("tomorrow");

var_dump($date1 == $date2); // bool(false)
var_dump($date1 < $date2);  // bool(true)
var_dump($date1 > $date2);  // bool(false)
jurka
la source
14
+1 DateTime gère correctement les années bissextiles et les fuseaux horaires et il y a un bon livre pour l'étagère: phparch.com/books/…
hakre
3
Existe-t-il une méthode qui donne le nombre total de secondes entre les deux DateTimes? (sans additionner les composants, c'est-à-dire)
pomme de
1
@Panique Les $ interval-> jours et $ interval-> d sont des mesures différentes. votre commentaire ci-dessus est juste "montre le nombre total de jours (non divisé en années, mois et jours comme ci-dessus)"
jurka
1
@potatoe Vous voulez probablement $date2->format('U') - $date1->format('U').
Paulo Freitas
3
notez qu'il y a un bogue où DateInterval a une propriété jours incorrecte ( toujours 6015 ) sous Windows avec certaines versions de PHP: bugs.php.net/bug.php?id=51184 (référez-vous aux commentaires pour correction / solution)
Pim Schaaf
73

La meilleure solution consiste à utiliser les objets PHP DateTime(et DateInterval). Chaque date est encapsulée dans un DateTimeobjet, puis une différence entre les deux peut être faite:

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

L' DateTimeobjet acceptera n'importe quel format strtotime(). Si un format de date encore plus spécifique est nécessaire, DateTime::createFromFormat()peut être utilisé pour créer leDateTime objet.

Une fois les deux objets instanciés, vous soustrayez l'un de l'autre avec DateTime::diff().

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

$differencecontient maintenant un DateIntervalobjet avec les informations de différence. A var_dump()ressemble à ceci:

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 l' DateIntervalobjet, nous devons vérifier chaque valeur et l'exclure si elle est 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;
}

Il ne reste plus qu'à appeler notre fonction sur l' $difference DateIntervalobjet:

echo format_interval($difference);

Et nous obtenons le résultat correct:

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);
Le fantôme de Madara
la source
DateTime()n'est pas une fonction, c'est un objet, et il est là depuis PHP 5.2. Assurez-vous que votre serveur le prend en charge.
Madara's Ghost du
2
@SecondRikudo DateTime :: Diff nécessite PHP 5.3.0
PhoneixS
Nous avons un problème, échangeons first_date à 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 obtenir le même résultat.
EgoistDeveloper
Parce que diff vous donne la différence entre les deux fois. La différence n'est pas 0 quelle que soit la date qui vient plus tard.
Madara's Ghost
1
C'est une très bonne réponse car elle fournit une fonction claire qui peut être appelée de n'importe où dans une base de code sans beaucoup de calculs de temps. D'autres réponses vous permettent de supprimer les calculs en écho à la volée qui répondent aux symptômes plutôt que de résoudre le problème ... Le seul élément que j'ai ajouté (et à peu près tous les autres articles ne couvrent pas cela) est la pluralisation des éléments $ interval si plus de 1.
nickhar
33

Afficher les heures et 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); 
Khaldonno
la source
7
Cela ne donnera probablement pas le résultat exact.
Dolphin
8
Et c'est une solution terrible à moins que vous ne soyez obligé d'utiliser une version terriblement obsolète de PHP ...
rdlowrey
2
Pas si SEC . Par exemple, 60 * 60 * 24 est répété 15 fois. Réutilisation du copier-coller longue durée!
Peter Mortensen
Et les années bissextiles? Une année n'est pas en moyenne 365 jours.
Peter Mortensen
Ce code suppose qu'un mois correspond à 30 jours en moyenne. Même en supposant 365 jours pour un an, un mois moyen est de 365/12 = 30,42 jours (environ).
Peter Mortensen
18

Jetez un œil au lien suivant. C'est la meilleure réponse que j'ai trouvée jusqu'à présent .. :)

function dateDiff ($d1, $d2) {

    // Return the number of days between the two dates:    
    return round(abs(strtotime($d1) - strtotime($d2))/86400);

} // end function dateDiff

Peu importe la date antérieure ou ultérieure lorsque vous transmettez les paramètres de date. La fonction utilise la valeur absolue PHP ABS () pour toujours renvoyer un nombre positif comme le nombre de jours entre les deux dates.

Gardez à l'esprit que le nombre de jours entre les deux dates N'INCLUT PAS les deux dates. Donc, si vous recherchez le nombre de jours représentés par toutes les dates comprises entre les dates entrées, y compris, vous devrez ajouter un (1) au résultat de cette fonction.

Par exemple, la différence (telle que renvoyée par la fonction ci-dessus) entre 2013-02-09 et 2013-02-14 est 5. Mais le nombre de jours ou de dates représenté par la plage de dates 2013-02-09 - 2013-02- 14 est 6.

http://www.bizinfosys.com/php/date-difference.html

casper123
la source
La question demandait la différence comme le nombre d'années, de mois et de jours, pas le nombre total de jours.
Peter Mortensen
14

J'ai voté pour Jurka de réponse que c'est mon préféré, mais j'ai une version pré-php.5.3 ...

Je me suis retrouvé à travailler sur un problème similaire - c'est ainsi que je suis arrivé à cette question en premier lieu - mais j'avais juste besoin d'une différence d'heures. Mais ma fonction a aussi bien résolu celui-ci et je n'ai nulle part dans ma propre bibliothèque pour le garder où il ne sera pas perdu et oublié, alors ... j'espère que cela sera utile à quelqu'un.

/**
 *
 * @param DateTime $oDate1
 * @param DateTime $oDate2
 * @return array 
 */
function date_diff_array(DateTime $oDate1, DateTime $oDate2) {
    $aIntervals = array(
        'year'   => 0,
        'month'  => 0,
        'week'   => 0,
        'day'    => 0,
        'hour'   => 0,
        'minute' => 0,
        'second' => 0,
    );

    foreach($aIntervals as $sInterval => &$iInterval) {
        while($oDate1 <= $oDate2){ 
            $oDate1->modify('+1 ' . $sInterval);
            if ($oDate1 > $oDate2) {
                $oDate1->modify('-1 ' . $sInterval);
                break;
            } else {
                $iInterval++;
            }
        }
    }

    return $aIntervals;
}

Et le test:

$oDate = new DateTime();
$oDate->modify('+111402189 seconds');
var_dump($oDate);
var_dump(date_diff_array(new DateTime(), $oDate));

Et le résultat:

object(DateTime)[2]
  public 'date' => string '2014-04-29 18:52:51' (length=19)
  public 'timezone_type' => int 3
  public 'timezone' => string 'America/New_York' (length=16)

array
  'year'   => int 3
  'month'  => int 6
  'week'   => int 1
  'day'    => int 4
  'hour'   => int 9
  'minute' => int 3
  'second' => int 8

J'ai l'idée originale d' ici , que j'ai modifiée pour mes utilisations (et j'espère que ma modification s'affichera également sur cette page).

Vous pouvez très facilement supprimer les intervalles dont vous ne voulez pas (par exemple "semaine") en les supprimant du $aIntervalstableau, ou peut-être en ajoutant un $aExcludeparamètre, ou en les filtrant simplement lorsque vous sortez la chaîne.

enobrev
la source
Malheureusement, cela ne renvoie pas la même chose que DateInterval en raison de débordements année / mois.
Stephen Harris
2
@StephenHarris: Je n'ai pas testé cela, mais en lisant le code, je suis assez confiant qu'il devrait retourner le même résultat - à condition que vous supprimiez l' weekindex $aIntervals(car DateDiffn'utilise jamais cela).
Alix Axel
C'est une excellente solution pour trouver des dates qui se produisent à chaque intervalle entre deux dates.
cerveau
14
<?php
    $today = strtotime("2011-02-03 00:00:00");
    $myBirthDate = strtotime("1964-10-30 00:00:00");
    printf("Days since my birthday: ", ($today - $myBirthDate)/60/60/24);
?>
vengat
la source
La question demandait la différence en nombre d' années , de mois et de jours . Cela génère la différence en tant que nombre total de jours.
Peter Mortensen
11

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 empêcher de réinventer la roue.

Par exemple, CodeIgniter a la timespan()fonction. Entrez simplement deux horodatages Unix et cela générera 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

Jake Wilson
la source
8

Exemple d'utilisation:

echo time_diff_string('2013-05-01 00:22:35', 'now');
echo time_diff_string('2013-05-01 00:22:35', 'now', true);

Production :

4 months ago
4 months, 2 weeks, 3 days, 1 hour, 49 minutes, 15 seconds ago

Une fonction :

function time_diff_string($from, $to, $full = false) {
    $from = new DateTime($from);
    $to = new DateTime($to);
    $diff = $to->diff($from);

    $diff->w = floor($diff->d / 7);
    $diff->d -= $diff->w * 7;

    $string = array(
        'y' => 'year',
        'm' => 'month',
        'w' => 'week',
        'd' => 'day',
        'h' => 'hour',
        'i' => 'minute',
        's' => 'second',
    );
    foreach ($string as $k => &$v) {
        if ($diff->$k) {
            $v = $diff->$k . ' ' . $v . ($diff->$k > 1 ? 's' : '');
        } else {
            unset($string[$k]);
        }
    }

    if (!$full) $string = array_slice($string, 0, 1);
    return $string ? implode(', ', $string) . ' ago' : 'just now';
}
Glavić
la source
si je veux déterminer si la différence est supérieure à 30 minutes, que dois-je faire?
Ofir Attia
@OfirAttia: vous avez un tas de questions comme ça ici sur SO, utilisez simplement la recherche. Démo simple
Glavić
7

J'ai une logique simple pour cela:

<?php
    per_days_diff('2011-12-12','2011-12-29')
    function per_days_diff($start_date, $end_date) {
        $per_days = 0;
        $noOfWeek = 0;
        $noOfWeekEnd = 0;
        $highSeason=array("7", "8");

        $current_date = strtotime($start_date);
        $current_date += (24 * 3600);
        $end_date = strtotime($end_date);

        $seassion = (in_array(date('m', $current_date), $highSeason))?"2":"1";

        $noOfdays = array('');

        while ($current_date <= $end_date) {
            if ($current_date <= $end_date) {
                $date = date('N', $current_date);
                array_push($noOfdays,$date);
                $current_date = strtotime('+1 day', $current_date);
            }
        }

        $finalDays = array_shift($noOfdays);
        //print_r($noOfdays);
        $weekFirst = array("week"=>array(),"weekEnd"=>array());
        for($i = 0; $i < count($noOfdays); $i++)
        {
            if ($noOfdays[$i] == 1)
            {
                //echo "This is week";
                //echo "<br/>";
                if($noOfdays[$i+6]==7)
                {
                    $noOfWeek++;
                    $i=$i+6;
                }
                else
                {
                    $per_days++;
                }
                //array_push($weekFirst["week"],$day);
            }
            else if($noOfdays[$i]==5)
            {
                //echo "This is weekend";
                //echo "<br/>";
                if($noOfdays[$i+2] ==7)
                {
                    $noOfWeekEnd++;
                    $i = $i+2;
                }
                else
                {
                    $per_days++;
                }
                //echo "After weekend value:- ".$i;
                //echo "<br/>";
            }
            else
            {
                $per_days++;
            }
        }

        /*echo $noOfWeek;
          echo "<br/>";
          echo $noOfWeekEnd;
          echo "<br/>";
          print_r($per_days);
          echo "<br/>";
          print_r($weekFirst);
        */

        $duration = array("weeks"=>$noOfWeek, "weekends"=>$noOfWeekEnd, "perDay"=>$per_days, "seassion"=>$seassion);
        return $duration;
      ?>
Hardik Raval
la source
Il semble qu'il manque quelque chose à la fin de l'exemple de code (une accolade de fin et " ?> "?).
Peter Mortensen
logique "simple". Ce sont au moins 40 lignes de code pur.
Madjosz
6

Vous pouvez utiliser le

getdate()

fonction qui retourne un tableau contenant tous les éléments de la date / heure fournie:

$diff = abs($endDate - $startDate);
$my_t=getdate($diff);
print("$my_t[year] years, $my_t[month] months and $my_t[mday] days");

Si vos dates de début et de fin sont au format chaîne, utilisez

$startDate = strtotime($startDateStr);
$endDate = strtotime($endDateStr);

avant le code ci-dessus

Mark Pim
la source
ne semble pas fonctionner. J'ai une date au début de l'ère de l'horodatage.
Sirber
Il est important de comprendre que vous devez faire un $my_t["year"] -= 1970pour obtenir le bon nombre d'années. Vous devez également soustraire votre différence d'heure de GMT pour obtenir les bonnes heures. Vous devez également soustraire 1 du mois et de la date.
Salman A
6
// If you just want to see the year difference then use this function.
// Using the logic I've created you may also create month and day difference
// which I did not provide here so you may have the efforts to use your brain.
// :)
$date1='2009-01-01';
$date2='2010-01-01';
echo getYearDifference ($date1,$date2);
function getYearDifference($date1=strtotime($date1),$date2=strtotime($date2)){
    $year = 0;
    while($date2 > $date1 = strtotime('+1 year', $date1)){
        ++$year;
    }
    return $year;
}
Esc
la source
"Strtotime ('+ 1 an', $ date1)" prend-il en compte les années bissextiles?
Peter Mortensen
6

C'est ma fonction. PHP requis> = 5.3.4. Il utilise la classe DateTime. Très rapide, rapide et peut faire la différence entre deux dates ou même le soi-disant "time since".

if(function_exists('grk_Datetime_Since') === FALSE){
    function grk_Datetime_Since($From, $To='', $Prefix='', $Suffix=' ago', $Words=array()){
        #   Est-ce qu'on calcul jusqu'à un moment précis ? Probablement pas, on utilise maintenant
        if(empty($To) === TRUE){
            $To = time();
        }

        #   On va s'assurer que $From est numérique
        if(is_int($From) === FALSE){
            $From = strtotime($From);
        };

        #   On va s'assurer que $To est numérique
        if(is_int($To) === FALSE){
            $To = strtotime($To);
        }

        #   On a une erreur ?
        if($From === FALSE OR $From === -1 OR $To === FALSE OR $To === -1){
            return FALSE;
        }

        #   On va créer deux objets de date
        $From = new DateTime(@date('Y-m-d H:i:s', $From), new DateTimeZone('GMT'));
        $To   = new DateTime(@date('Y-m-d H:i:s', $To), new DateTimeZone('GMT'));

        #   On va calculer la différence entre $From et $To
        if(($Diff = $From->diff($To)) === FALSE){
            return FALSE;
        }

        #   On va merger le tableau des noms (par défaut, anglais)
        $Words = array_merge(array(
            'year'      => 'year',
            'years'     => 'years',
            'month'     => 'month',
            'months'    => 'months',
            'week'      => 'week',
            'weeks'     => 'weeks',
            'day'       => 'day',
            'days'      => 'days',
            'hour'      => 'hour',
            'hours'     => 'hours',
            'minute'    => 'minute',
            'minutes'   => 'minutes',
            'second'    => 'second',
            'seconds'   => 'seconds'
        ), $Words);

        #   On va créer la chaîne maintenant
        if($Diff->y > 1){
            $Text = $Diff->y.' '.$Words['years'];
        } elseif($Diff->y == 1){
            $Text = '1 '.$Words['year'];
        } elseif($Diff->m > 1){
            $Text = $Diff->m.' '.$Words['months'];
        } elseif($Diff->m == 1){
            $Text = '1 '.$Words['month'];
        } elseif($Diff->d > 7){
            $Text = ceil($Diff->d/7).' '.$Words['weeks'];
        } elseif($Diff->d == 7){
            $Text = '1 '.$Words['week'];
        } elseif($Diff->d > 1){
            $Text = $Diff->d.' '.$Words['days'];
        } elseif($Diff->d == 1){
            $Text = '1 '.$Words['day'];
        } elseif($Diff->h > 1){
            $Text = $Diff->h.' '.$Words['hours'];
        } elseif($Diff->h == 1){
            $Text = '1 '.$Words['hour'];
        } elseif($Diff->i > 1){
            $Text = $Diff->i.' '.$Words['minutes'];
        } elseif($Diff->i == 1){
            $Text = '1 '.$Words['minute'];
        } elseif($Diff->s > 1){
            $Text = $Diff->s.' '.$Words['seconds'];
        } else {
            $Text = '1 '.$Words['second'];
        }

        return $Prefix.$Text.$Suffix;
    }
}
David Bélanger
la source
6

Je préfère utiliser date_createetdate_diff objets.

Code:

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

$dateDifference = date_diff($date1, $date2)->format('%y years, %m months and %d days');

echo $dateDifference;

Production:

2 years, 3 months and 2 days

Pour plus d'informations, lisez le date_diffmanuel PHP

Selon le manuel date_diffest un alias de DateTime :: diff ()

Adeel
la source
5

Cela tentera de détecter si un horodatage a été donné ou non, et renverra également les dates / heures futures sous forme de valeurs négatives:

<?php

function time_diff($start, $end = NULL, $convert_to_timestamp = FALSE) {
  // If $convert_to_timestamp is not explicitly set to TRUE,
  // check to see if it was accidental:
  if ($convert_to_timestamp || !is_numeric($start)) {
    // If $convert_to_timestamp is TRUE, convert to timestamp:
    $timestamp_start = strtotime($start);
  }
  else {
    // Otherwise, leave it as a timestamp:
    $timestamp_start = $start;
  }
  // Same as above, but make sure $end has actually been overridden with a non-null,
  // non-empty, non-numeric value:
  if (!is_null($end) && (!empty($end) && !is_numeric($end))) {
    $timestamp_end = strtotime($end);
  }
  else {
    // If $end is NULL or empty and non-numeric value, assume the end time desired
    // is the current time (useful for age, etc):
    $timestamp_end = time();
  }
  // Regardless, set the start and end times to an integer:
  $start_time = (int) $timestamp_start;
  $end_time = (int) $timestamp_end;

  // Assign these values as the params for $then and $now:
  $start_time_var = 'start_time';
  $end_time_var = 'end_time';
  // Use this to determine if the output is positive (time passed) or negative (future):
  $pos_neg = 1;

  // If the end time is at a later time than the start time, do the opposite:
  if ($end_time <= $start_time) {
    $start_time_var = 'end_time';
    $end_time_var = 'start_time';
    $pos_neg = -1;
  }

  // Convert everything to the proper format, and do some math:
  $then = new DateTime(date('Y-m-d H:i:s', $$start_time_var));
  $now = new DateTime(date('Y-m-d H:i:s', $$end_time_var));

  $years_then = $then->format('Y');
  $years_now = $now->format('Y');
  $years = $years_now - $years_then;

  $months_then = $then->format('m');
  $months_now = $now->format('m');
  $months = $months_now - $months_then;

  $days_then = $then->format('d');
  $days_now = $now->format('d');
  $days = $days_now - $days_then;

  $hours_then = $then->format('H');
  $hours_now = $now->format('H');
  $hours = $hours_now - $hours_then;

  $minutes_then = $then->format('i');
  $minutes_now = $now->format('i');
  $minutes = $minutes_now - $minutes_then;

  $seconds_then = $then->format('s');
  $seconds_now = $now->format('s');
  $seconds = $seconds_now - $seconds_then;

  if ($seconds < 0) {
    $minutes -= 1;
    $seconds += 60;
  }
  if ($minutes < 0) {
    $hours -= 1;
    $minutes += 60;
  }
  if ($hours < 0) {
    $days -= 1;
    $hours += 24;
  }
  $months_last = $months_now - 1;
  if ($months_now == 1) {
    $years_now -= 1;
    $months_last = 12;
  }

  // "Thirty days hath September, April, June, and November" ;)
  if ($months_last == 9 || $months_last == 4 || $months_last == 6 || $months_last == 11) {
    $days_last_month = 30;
  }
  else if ($months_last == 2) {
    // Factor in leap years:
    if (($years_now % 4) == 0) {
      $days_last_month = 29;
    }
    else {
      $days_last_month = 28;
    }
  }
  else {
    $days_last_month = 31;
  }
  if ($days < 0) {
    $months -= 1;
    $days += $days_last_month;
  }
  if ($months < 0) {
    $years -= 1;
    $months += 12;
  }

  // Finally, multiply each value by either 1 (in which case it will stay the same),
  // or by -1 (in which case it will become negative, for future dates).
  // Note: 0 * 1 == 0 * -1 == 0
  $out = new stdClass;
  $out->years = (int) $years * $pos_neg;
  $out->months = (int) $months * $pos_neg;
  $out->days = (int) $days * $pos_neg;
  $out->hours = (int) $hours * $pos_neg;
  $out->minutes = (int) $minutes * $pos_neg;
  $out->seconds = (int) $seconds * $pos_neg;
  return $out;
}

Exemple d'utilisation:

<?php
  $birthday = 'June 2, 1971';
  $check_age_for_this_date = 'June 3, 1999 8:53pm';
  $age = time_diff($birthday, $check_age_for_this_date)->years;
  print $age;// 28

Ou:

<?php
  $christmas_2020 = 'December 25, 2020';
  $countdown = time_diff($christmas_2020);
  print_r($countdown);
jerdiggity
la source
5

"si" la date est stockée dans MySQL, je trouve plus facile de faire le calcul de la différence au niveau de la base de données ... Ensuite, en fonction des sorties Jour, Heure, Min, Sec, analyser et afficher les résultats selon le cas ...

mysql> select firstName, convert_tz(loginDate, '+00:00', '-04:00') as loginDate, TIMESTAMPDIFF(DAY, loginDate, now()) as 'Day', TIMESTAMPDIFF(HOUR, loginDate, now())+4 as 'Hour', TIMESTAMPDIFF(MINUTE, loginDate, now())+(60*4) as 'Min', TIMESTAMPDIFF(SECOND, loginDate, now())+(60*60*4) as 'Sec' from User_ where userId != '10158' AND userId != '10198' group by emailAddress order by loginDate desc;
 +-----------+---------------------+------+------+------+--------+
 | firstName | loginDate           | Day  | Hour | Min  | Sec    |
 +-----------+---------------------+------+------+------+--------+
 | Peter     | 2014-03-30 18:54:40 |    0 |    4 |  244 |  14644 |
 | Keith     | 2014-03-30 18:54:11 |    0 |    4 |  244 |  14673 |
 | Andres    | 2014-03-28 09:20:10 |    2 |   61 | 3698 | 221914 |
 | Nadeem    | 2014-03-26 09:33:43 |    4 |  109 | 6565 | 393901 |
 +-----------+---------------------+------+------+------+--------+
 4 rows in set (0.00 sec)
ElasticThoughts
la source
5

J'ai trouvé votre article sur la page suivante, qui contient un certain nombre de références pour les calculs de date et d'heure PHP .

Calculez la différence entre deux dates (et l'heure) en utilisant PHP. La page suivante fournit une gamme de méthodes différentes (7 au total) pour effectuer des calculs de date / heure à l'aide de PHP, pour déterminer la différence de temps (heures, munites), jours, mois ou années entre deux dates.

Voir PHP Date Time - 7 méthodes pour calculer la différence entre 2 dates .

James - Développement Php
la source
4

Vous pouvez également utiliser le code suivant pour renvoyer la différence de date par des fractions rondes jusqu'à $ date1 = $ duedate; // attribue l'échéance echo $ date2 = date ("Ymd"); // date actuelle $ ts1 = strtotime ($ date1); $ ts2 = strtotime ($ date2); $ seconds_diff = $ ts1 - $ ts2; echo $ dateiff = ceil (($ seconds_diff / 3600) / 24); // retour en jours

Si vous utilisez la méthode de plancher de php au lieu de plafond, cela vous renverra la fraction ronde. Veuillez vérifier la différence ici, parfois si le fuseau horaire de vos serveurs de transfert est différent, le fuseau horaire du site en direct dans ce cas, vous pouvez obtenir des résultats différents, modifiez donc les conditions en conséquence.

Rikin Adhyapak
la source
4
$date1 = date_create('2007-03-24');
$date2 = date_create('2009-06-26');
$interval = date_diff($date1, $date2);
echo "difference : " . $interval->y . " years, " . $interval->m." months, ".$interval->d." days ";
liza
la source
4

vous pouvez toujours utiliser la fonction suivante qui peut renvoyer l'âge en années et mois (c'est-à-dire 1 an 4 mois)

function getAge($dob, $age_at_date)
{  
    $d1 = new DateTime($dob);
    $d2 = new DateTime($age_at_date);
    $age = $d2->diff($d1);
    $years = $age->y;
    $months = $age->m;

    return $years.'.'.months;
}

ou si vous voulez que l'âge soit calculé à la date actuelle, vous pouvez utiliser

function getAge($dob)
{  
    $d1 = new DateTime($dob);
    $d2 = new DateTime(date());
    $age = $d2->diff($d1);
    $years = $age->y;
    $months = $age->m;

    return $years.'.'.months;
}
Rickus Harmse
la source
4

Pour la version php> = 5.3: Créez deux objets de date puis utilisez la date_diff()fonction. Il renverra l' objet php DateInterval . voir la documentation

$date1=date_create("2007-03-24");
$date2=date_create("2009-06-26");
$diff=date_diff($date1,$date2);
echo $diff->format("%R%a days");
bikram kc
la source
4

Voici le code exécutable

$date1 = date_create('2007-03-24');
$date2 = date_create('2009-06-26');
$diff1 = date_diff($date1,$date2);
$daysdiff = $diff1->format("%R%a");
$daysdiff = abs($daysdiff);
Mosin
la source
3

J'ai eu le même problème avec PHP 5.2 et l'ai résolu avec MySQL. Peut-être pas exactement ce que vous cherchez, mais cela fera l'affaire et renverra le nombre de jours:

$datediff_q = $dbh->prepare("SELECT DATEDIFF(:date2, :date1)");
$datediff_q->bindValue(':date1', '2007-03-24', PDO::PARAM_STR);
$datediff_q->bindValue(':date2', '2009-06-26', PDO::PARAM_STR);
$datediff = ($datediff_q->execute()) ? $datediff_q->fetchColumn(0) : false;

Plus d'informations ici http://dev.mysql.com/doc/refman/5.5/en/date-and-time-functions.html#function_datediff

Klemen Tušar
la source
3

Puisque tout le monde publie des exemples de code, voici une autre version.

Je voulais une fonction pour afficher les différences de quelques secondes à plusieurs années (une seule unité). Pour les périodes de plus d'un jour, je voulais qu'il se déroule à minuit (10h lundi vu de 9h mercredi il y a 2 jours, pas 1). Et pour des périodes de plus d'un mois, je voulais que le roulement se fasse le même jour du mois (y compris pour les mois 30/31 jours et les années bissextiles).

Voici ce que j'ai trouvé:

/**
 * Returns how long ago something happened in the past, showing it
 * as n seconds / minutes / hours / days / weeks / months / years ago.
 *
 * For periods over a day, it rolls over at midnight (so doesn't depend
 * on current time of day), and it correctly accounts for month-lengths
 * and leap-years (months and years rollover on current day of month).
 *
 * $param string $timestamp in DateTime format
 * $return string description of interval
 */
function ago($timestamp)
{
    $then = date_create($timestamp);

    // for anything over 1 day, make it rollover on midnight
    $today = date_create('tomorrow'); // ie end of today
    $diff = date_diff($then, $today);

    if ($diff->y > 0) return $diff->y.' year'.($diff->y>1?'s':'').' ago';
    if ($diff->m > 0) return $diff->m.' month'.($diff->m>1?'s':'').' ago';
    $diffW = floor($diff->d / 7);
    if ($diffW > 0) return $diffW.' week'.($diffW>1?'s':'').' ago';
    if ($diff->d > 1) return $diff->d.' day'.($diff->d>1?'s':'').' ago';

    // for anything less than 1 day, base it off 'now'
    $now = date_create();
    $diff = date_diff($then, $now);

    if ($diff->d > 0) return 'yesterday';
    if ($diff->h > 0) return $diff->h.' hour'.($diff->h>1?'s':'').' ago';
    if ($diff->i > 0) return $diff->i.' minute'.($diff->i>1?'s':'').' ago';
    return $diff->s.' second'.($diff->s==1?'':'s').' ago';
}
ChrisV
la source
3

Il y a quelque temps, j'ai écrit une format_datefonction car cela donne de nombreuses options sur la façon dont vous voulez votre date :

function format_date($date, $type, $seperator="-")
{
    if($date)
    {
        $day = date("j", strtotime($date));
        $month = date("n", strtotime($date));
        $year = date("Y", strtotime($date));
        $hour = date("H", strtotime($date));
        $min = date("i", strtotime($date));
        $sec = date("s", strtotime($date));

        switch($type)
        {
            case 0:  $date = date("Y".$seperator."m".$seperator."d",mktime($hour, $min, $sec, $month, $day, $year)); break;
            case 1:  $date = date("D, F j, Y",mktime($hour, $min, $sec, $month, $day, $year)); break;
            case 2:  $date = date("d".$seperator."m".$seperator."Y",mktime($hour, $min, $sec, $month, $day, $year)); break;
            case 3:  $date = date("d".$seperator."M".$seperator."Y",mktime($hour, $min, $sec, $month, $day, $year)); break;
            case 4:  $date = date("d".$seperator."M".$seperator."Y h:i A",mktime($hour, $min, $sec, $month, $day, $year)); break;
            case 5:  $date = date("m".$seperator."d".$seperator."Y",mktime($hour, $min, $sec, $month, $day, $year)); break;
            case 6:  $date = date("M",mktime($hour, $min, $sec, $month, $day, $year)); break;
            case 7:  $date = date("Y",mktime($hour, $min, $sec, $month, $day, $year)); break;
            case 8:  $date = date("j",mktime($hour, $min, $sec, $month, $day, $year)); break;
            case 9:  $date = date("n",mktime($hour, $min, $sec, $month, $day, $year)); break;
            case 10: 
                     $diff = abs(strtotime($date) - strtotime(date("Y-m-d h:i:s"))); 
                     $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));
                     $date = $years . " years, " . $months . " months, " . $days . "days";
        }
    }
    return($date);
}    
Champ
la source
2
Cette réponse est tout aussi fausse que la réponse de Khaldonno. Il suppose (cas 10) qu'une année a 365 jours (chaque quatrième année a 366 jours (sauf pour les règles de 100 ans / 400 ans pour le calendrier grégorien)), et qu'un mois a 30 jours (c'est environ 30,42 jours environ). les années non bissextiles). Même avec de meilleures constantes, il n'est correct qu'en moyenne, pas nécessairement correct pour deux dates particulières.
Peter Mortensen, le
3

Très simple:

    <?php
        $date1 = date_create("2007-03-24");
        echo "Start date: ".$date1->format("Y-m-d")."<br>";
        $date2 = date_create("2009-06-26");
        echo "End date: ".$date2->format("Y-m-d")."<br>";
        $diff = date_diff($date1,$date2);
        echo "Difference between start date and end date: ".$diff->format("%y years, %m months and %d days")."<br>";
    ?>

Veuillez vérifier le lien suivant pour plus de détails:

PHP: date_diff - Manuel

Notez que c'est pour PHP 5.3.0 ou supérieur.

Choudhury Saadmaan Mahmid
la source
3

Une fonction simple

function time_difference($time_1, $time_2, $limit = null)
{

    $val_1 = new DateTime($time_1);
    $val_2 = new DateTime($time_2);

    $interval = $val_1->diff($val_2);

    $output = array(
        "year" => $interval->y,
        "month" => $interval->m,
        "day" => $interval->d,
        "hour" => $interval->h,
        "minute" => $interval->i,
        "second" => $interval->s
    );

    $return = "";
    foreach ($output AS $key => $value) {

        if ($value == 1)
            $return .= $value . " " . $key . " ";
        elseif ($value >= 1)
            $return .= $value . " " . $key . "s ";

        if ($key == $limit)
            return trim($return);
    }
    return trim($return);
}

Utilisez comme

echo time_difference ($time_1, $time_2, "day");

Reviendra comme 2 years 8 months 2 days

Anuj
la source
3

Essayez cette réponse très simple en utilisant date_diff () , elle est testée.

$date1 = date_create("2017-11-27");
$date2 = date_create("2018-12-29");
$diff=date_diff($date1,$date2);
$months = $diff->format("%m months");
$years = $diff->format("%y years");
$days = $diff->format("%d days");

echo $years .' '.$months.' '.$days;

la sortie est:

1 years 1 months 2 days
GN
la source
2

J'utilise la fonction suivante que j'ai écrite, quand PHP 5.3 (respectivement date_diff ()) n'est pas disponible:

        function dateDifference($startDate, $endDate)
        {
            $startDate = strtotime($startDate);
            $endDate = strtotime($endDate);
            if ($startDate === false || $startDate < 0 || $endDate === false || $endDate < 0 || $startDate > $endDate)
                return false;

            $years = date('Y', $endDate) - date('Y', $startDate);

            $endMonth = date('m', $endDate);
            $startMonth = date('m', $startDate);

            // Calculate months
            $months = $endMonth - $startMonth;
            if ($months <= 0)  {
                $months += 12;
                $years--;
            }
            if ($years < 0)
                return false;

            // Calculate the days
            $measure = ($months == 1) ? 'month' : 'months';
            $days = $endDate - strtotime('+' . $months . ' ' . $measure, $startDate);
            $days = date('z', $days);   

            return array($years, $months, $days);
        }
Konstantin Shegunov
la source
2

DateInterval est super mais il a quelques mises en garde:

  1. uniquement pour PHP 5.3+ ( mais ce n'est vraiment plus une bonne excuse )
  2. prend en charge uniquement les années, mois, jours, heures, minutes et secondes (pas de semaines)
  3. il calcule la différence avec tous les + jours ci-dessus (vous ne pouvez pas obtenir la différence en mois seulement)

Pour surmonter cela, j'ai codé ce qui suit (amélioré par rapport à la réponse @enobrev ):

function date_dif($since, $until, $keys = 'year|month|week|day|hour|minute|second')
{
    $date = array_map('strtotime', array($since, $until));

    if ((count($date = array_filter($date, 'is_int')) == 2) && (sort($date) === true))
    {
        $result = array_fill_keys(explode('|', $keys), 0);

        foreach (preg_grep('~^(?:year|month)~i', $result) as $key => $value)
        {
            while ($date[1] >= strtotime(sprintf('+%u %s', $value + 1, $key), $date[0]))
            {
                ++$value;
            }

            $date[0] = strtotime(sprintf('+%u %s', $result[$key] = $value, $key), $date[0]);
        }

        foreach (preg_grep('~^(?:year|month)~i', $result, PREG_GREP_INVERT) as $key => $value)
        {
            if (($value = intval(abs($date[0] - $date[1]) / strtotime(sprintf('%u %s', 1, $key), 0))) > 0)
            {
                $date[0] = strtotime(sprintf('+%u %s', $result[$key] = $value, $key), $date[0]);
            }
        }

        return $result;
    }

    return false;
}

Il exécute deux boucles; le premier traite des intervalles relatifs (années et mois) via le forçage brut, et le second calcule les intervalles absolus supplémentaires avec une arithmétique simple (donc c'est plus rapide):

echo humanize(date_dif('2007-03-24', '2009-07-31', 'second')); // 74300400 seconds
echo humanize(date_dif('2007-03-24', '2009-07-31', 'minute|second')); // 1238400 minutes, 0 seconds
echo humanize(date_dif('2007-03-24', '2009-07-31', 'hour|minute|second')); // 20640 hours, 0 minutes, 0 seconds
echo humanize(date_dif('2007-03-24', '2009-07-31', 'year|day')); // 2 years, 129 days
echo humanize(date_dif('2007-03-24', '2009-07-31', 'year|week')); // 2 years, 18 weeks
echo humanize(date_dif('2007-03-24', '2009-07-31', 'year|week|day')); // 2 years, 18 weeks, 3 days
echo humanize(date_dif('2007-03-24', '2009-07-31')); // 2 years, 4 months, 1 week, 0 days, 0 hours, 0 minutes, 0 seconds

function humanize($array)
{
    $result = array();

    foreach ($array as $key => $value)
    {
        $result[$key] = $value . ' ' . $key;

        if ($value != 1)
        {
            $result[$key] .= 's';
        }
    }

    return implode(', ', $result);
}
Alix Axel
la source
@PeterMortensen: Cela devrait fonctionner, mais je ne donne aucune garantie. Réglez votre fuseau horaire et essayez-le.
Alix Axel