Como calcular a diferença entre duas datas usando PHP?

722

Eu tenho duas datas do formulário:

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

Agora, preciso encontrar a diferença entre esses dois da seguinte forma:

2 years, 3 months and 2 days

Como posso fazer isso em PHP?

Taryn
fonte
1
2 anos 94 dias. O cálculo dos meses, considerando os anos bissextos, seria problemático. Quão preciso isso precisa ser?
23410 dbasnett
possível duplicata de Como calcular o tempo relativo?
Cole Johnson

Respostas:

527

Use isso para código legado (PHP <5.3). Para uma solução atualizada, veja a resposta da jurka abaixo

Você pode usar strtotime () para converter duas datas em hora unix e, em seguida, calcular o número de segundos entre elas. A partir disso, é bastante fácil calcular diferentes períodos de tempo.

$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: Obviamente, a maneira preferida de fazer isso é como descrito por jurka abaixo. Meu código geralmente é recomendado apenas se você não tiver o PHP 5.3 ou melhor.

Várias pessoas nos comentários apontaram que o código acima é apenas uma aproximação. Eu ainda acredito que, para a maioria dos propósitos, tudo bem, já que o uso de um intervalo é mais para fornecer uma noção de quanto tempo se passou ou resta, em vez de fornecer precisão - se você quiser fazer isso, basta exibir a data.

Apesar de tudo, decidi abordar as reclamações. Se você realmente precisa de um intervalo exato, mas não tem acesso ao PHP 5.3, use o código abaixo (ele também deve funcionar no PHP 4). Esta é uma porta direta do código que o PHP usa internamente para calcular intervalos, com a exceção de que não leva em consideração o horário de verão. Isso significa que ele estará desligado por uma hora no máximo, mas, exceto por isso, deve estar correto.

<?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
fonte
1
Se você estiver usando a classe DateTime, poderá usar o formato $ date-> ('U') para obter o registro de data e hora do unix.
9139 Jon Cram
4
Não é verdade se você tiver que lidar com o horário de verão / inverno. Nesse caso específico, quando você ajusta o horário de verão / inverno, um dia é igual a 23 ou 25 horas.
Arno
4
Bem, o mesmo argumento poderia ser feito por anos bissextos. Também não leva isso em consideração. Ainda assim, não estou convencido de que você queira levar isso em consideração, já que estamos discutindo uma série aqui. A semântica para um intervalo é um pouco diferente do que para uma data absoluta.
Emil H
9
Esta função está incorreta. É bom para uma aproximação, mas incorreto para intervalos exatos. Por um lado, parte do princípio de que há 30 dias em um mês, ou seja, terá a mesma diferença de dias entre 1º de fevereiro e 1º de março do que entre 1º de julho e 1º de agosto (independentemente do ano bissexto).
enobrev
1
No PHP, as variáveis ​​de referência estão na assinatura da função, não na chamada. Mova todo o seu &para as assinaturas.
Paul Tarjan
909

Sugiro usar objetos DateTime e 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 ";

leia mais php DateTime :: diff manual

Do manual:

A partir do PHP 5.2.2, os objetos DateTime podem ser comparados usando operadores de comparação.

$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
fonte
14
+1 DateTime anos alças bissextos e fusos horários corretamente e não é um bom livro para a prateleira: phparch.com/books/...
hakre
3
Existe um método que fornece o número total de segundos entre os dois DateTimes? (sem somar os componentes)
potatoe
1
@Panique O $ interval-> days e $ interval-> d são medidas diferentes. seu comentário acima está correto "mostra a quantidade total de dias (não divididos em anos, meses e dias como acima)"
jurka
1
@ Potatoe Você provavelmente quer $date2->format('U') - $date1->format('U').
Paulo Freitas
3
observe que há um erro em que DateInterval tem uma propriedade de dias incorretos ( sempre 6015 ) no Windows com algumas versões do PHP: bugs.php.net/bug.php?id=51184 (consulte os comentários para correção / solução alternativa)
Pim Schaaf
73

O melhor curso de ação é usar objetos DateTime(e DateInterval) do PHP . Cada data é encapsulada em um DateTimeobjeto e, em seguida, uma diferença entre as duas pode ser feita:

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

O DateTimeobjeto aceitará qualquer formato aceitável strtotime(). Se um formato de data ainda mais específico for necessário, DateTime::createFromFormat()pode ser usado para criar o DateTimeobjeto.

Depois que os dois objetos foram instanciados, você subtrai um do outro com DateTime::diff().

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

$differenceagora mantém um DateIntervalobjeto com as informações de diferença. A var_dump()é assim:

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

Para formatar o DateIntervalobjeto, precisamos verificar cada valor e excluí-lo se for 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;
}

Tudo o que resta agora é chamar nossa função no $difference DateIntervalobjeto:

echo format_interval($difference);

E obtemos o resultado correto:

20 dias 6 horas 56 minutos 30 segundos

O código completo usado para alcançar a meta:

/**
 * 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);
Fantasma de Madara
fonte
DateTime()não é uma função, é um objeto e existe desde o PHP 5.2. Verifique se o seu servidor suporta isso.
Ghost de Madara
2
@SecondRikudo DateTime :: Diff need PHP 5.3.0
PhoneixS
Temos um problema, troque first_date por second_date e estamos obtendo o mesmo resultado? Por que não dizer 0 dias 0 horas 0 minutos 0 segundos ou apenas 0. Exemplo: 2012-11-30 17:03:30 - 2012-12-21 00:00:00 e 2012-12-21 00:00:00 - 2012-11-30 17:03:30 obter o mesmo resultado.
EgoistDeveloper
Porque diff lhe dá a diferença entre os dois tempos. A diferença não é 0, independentemente da data que vier depois.
Ghost de Madara
1
Essa é uma resposta muito boa, pois fornece uma função clara que pode ser chamada de qualquer lugar em uma base de código sem muito tempo. Outras respostas permitem que você solte cálculos repetidos que abordam os sintomas em vez de resolver o problema ... O único elemento que eu adicionei (e praticamente todas as outras postagens não cobrem isso) é a pluralização de elementos de intervalo $ se mais do que 1.
nickhar
33

Exibir horas, minutos e segundos.

$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
fonte
7
Provavelmente isso não dará o resultado exato.
Dolphin
8
E é uma solução terrível a menos que você é forçado a usar uma versão terrivelmente ultrapassada de PHP ...
rdlowrey
2
Não é tão seco . Por exemplo, 60 * 60 * 24 é repetido 15 vezes. Vida útil reutilização de copiar e colar!
31516 Peter Mortensen
E os anos bissextos? Um ano não é de 365 dias em média.
Peter Mortensen
Este código pressupõe que um mês é de 30 dias em média. Mesmo assumindo 365 dias por um ano, um mês médio é 365/12 = 30,42 dias (aprox.).
Peter Mortensen
18

Dê uma olhada no seguinte link. Esta é a melhor resposta que eu encontrei até agora .. :)

function dateDiff ($d1, $d2) {

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

} // end function dateDiff

Não importa qual data é anterior ou posterior quando você passa os parâmetros de data. A função usa o valor absoluto PHP ABS () para sempre retornar um número positivo como o número de dias entre as duas datas.

Lembre-se de que o número de dias entre as duas datas NÃO inclui as duas datas. Portanto, se você estiver procurando o número de dias representado por todas as datas entre e incluindo as datas inseridas, será necessário adicionar um (1) ao resultado dessa função.

Por exemplo, a diferença (retornada pela função acima) entre 09/02/2013 e 14/02/2013 é 5. Mas o número de dias ou datas representados pelo período 2013-02-09 - 2013-02- 14 é 6.

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

casper123
fonte
A pergunta pedia a diferença como o número de anos, meses e dias, não o número total de dias.
31516 Peter Mortensen
14

Eu votei para Jurka 's resposta como esse é o meu favorito, mas eu tenho uma versão pré-php.5.3 ...

Eu me peguei trabalhando em um problema semelhante - e foi assim que cheguei a essa pergunta em primeiro lugar - mas só precisava de uma diferença de horas. Mas minha função também resolveu essa questão muito bem e eu não tenho nenhum lugar na minha própria biblioteca para mantê-la onde não seja perdida e esquecida, então ... espero que isso seja útil para alguém.

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

E o teste:

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

E o resultado:

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

Eu tive a idéia original daqui , que modifiquei para meus usos (e espero que minha modificação seja mostrada nessa página também).

Você pode remover com facilidade os intervalos que não deseja (digamos "semana") removendo-os da $aIntervalsmatriz, ou talvez adicionando um $aExcludeparâmetro, ou apenas filtrando-os ao produzir a string.

enobrev
fonte
Infelizmente, isso não retorna a mesma coisa que DateInterval devido a estouros de ano / mês.
18712 Stephen
2
@ StephenHarris: Eu não testei isso, mas lendo o código, estou bastante confiante de que deve retornar o mesmo resultado - desde que você exclua o weekíndice $aIntervals(já que DateDiffnunca o usa).
Alix Axel
Essa é uma ótima solução para encontrar datas que ocorrem a cada intervalo entre duas datas.
betweenbrain
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
fonte
A pergunta pedia a diferença conforme o número de anos , meses e dias . Isso gera a diferença como o número total de dias.
22416 Peter Mortensen
11

Não sei se você está usando uma estrutura PHP ou não, mas muitas estruturas PHP têm bibliotecas de data / hora e ajudantes para ajudar a impedir que você reinvente a roda.

Por exemplo, CodeIgniter tem a timespan()função Basta inserir dois carimbos de data e hora do Unix e ele gerará automaticamente um resultado como este:

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

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

Jake Wilson
fonte
8

Use exemplo:

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

Resultado :

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

Função:

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ć
fonte
se eu quiser determinar se a diferença é maior que 30 minutos, o que devo fazer?
Ofir Attia
@OfirAttia: você tem um monte de perguntas como essa aqui no SO, basta usar a pesquisa. Demonstração simples
Glavić
7

Eu tenho uma lógica simples para isso:

<?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
fonte
Parece haver algo faltando no final do código de exemplo (uma chave final e " ?> "?).
22416 Peter Mortensen
lógica "simples". Essas são pelo menos 40 linhas de código puro.
Madjosz
6

Você pode usar o

getdate()

função que retorna uma matriz contendo todos os elementos da data / hora fornecida:

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

Se as datas de início e término estiverem no formato de sequência, use

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

antes do código acima

Mark Pim
fonte
parece não funcionar. Recebo uma data no início da era do carimbo de data / hora.
Sirber
É importante entender que você precisa fazer um $my_t["year"] -= 1970para obter o número correto de anos. Você também precisa subtrair sua diferença horária da GMT para acertar as horas. Você precisa subtrair 1 do mês e da data também.
Salman Um
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
fonte
"Strtotime ('+ 1 ano', $ date1)" leva anos bissextos em consideração?
31516 Peter Mortensen
6

Esta é a minha função. PHP necessário> = 5.3.4. Ele usa a classe DateTime. Muito rápido, rápido e pode fazer a diferença entre duas datas ou até o chamado "tempo desde".

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
fonte
6

Eu preferiria usar date_createe date_diffobjetos.

Código:

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

Resultado:

2 years, 3 months and 2 days

Para mais informações, leia o manual do PHPdate_diff

De acordo com manual, date_diffé um alias de DateTime :: diff ()

Adeel
fonte
5

Isso tentará detectar se um carimbo de data / hora foi fornecido ou não e também retornará datas / horas futuras como valores negativos:

<?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;
}

Exemplo de uso:

<?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
fonte
5

"if" a data é armazenada no MySQL, acho mais fácil fazer o cálculo da diferença no nível do banco de dados ... Então, com base na saída Dia, Hora, Min, Seg, analise e exiba os resultados conforme apropriado ...

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
fonte
5

Encontrei seu artigo na página a seguir, que contém várias referências para cálculos de data e hora do PHP .

Calcule a diferença entre duas datas (e hora) usando PHP. A página a seguir fornece uma variedade de métodos diferentes (7 no total) para executar cálculos de data / hora usando PHP, para determinar a diferença de tempo (horas, munites), dias, meses ou anos entre duas datas.

Veja PHP Date Time - 7 Métodos para calcular a diferença entre 2 datas .

James - Desenvolvimento Php
fonte
4

Você também pode usar o código a seguir para retornar a diferença de datas por frações arredondadas até $ date1 = $ dededate; // atribui data de vencimento echo $ date2 = date ("Ymd"); // data atual $ ts1 = strtotime ($ date1); $ ts2 = período de tempo ($ date2); $ seconds_diff = $ ts1 - $ ts2; echo $ datediff = ceil (($ seconds_diff / 3600) / 24); // retorna em dias

Se você usar o método floor do php em vez do ceil, ele retornará a fração redonda para baixo. Verifique a diferença aqui, algumas vezes, se o fuso horário dos servidores de armazenamento temporário for diferente do fuso horário do site ativo, nesse caso, você poderá obter resultados diferentes, portanto altere as condições adequadamente.

Rikin Adhyapak
fonte
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
fonte
4

você sempre pode usar a seguinte função que pode retornar a idade em anos e meses (ou seja, 1 ano 4 meses)

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 se desejar que a idade seja calculada na data atual, você pode usar

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
fonte
4

Para a versão php> = 5.3: Crie dois objetos de data e use a date_diff()função Ele retornará o objeto php DateInterval . ver documentação

$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
fonte
4

Aqui está o código executável

$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
fonte
3

Eu tive o mesmo problema com o PHP 5.2 e resolvi com o MySQL. Pode não ser exatamente o que você está procurando, mas isso funcionará e retornará o número de dias:

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

Mais informações aqui http://dev.mysql.com/doc/refman/5.5/en/date-and-time-functions.html#function_datediff

Klemen Tušar
fonte
3

Como todo mundo está postando amostras de código, aqui está outra versão.

Eu queria que uma função exibisse diferenças de segundos a anos (apenas uma unidade). Por períodos superiores a 1 dia, eu queria que ele rolasse à meia-noite (10h na segunda-feira, das 9h na quarta-feira, há 2 dias atrás, não 1). E, por períodos superiores a um mês, eu queria que a rolagem ocorresse no mesmo dia do mês (inclusive por 30/31 dias meses e anos bissextos).

Isto é o que eu vim com:

/**
 * 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
fonte
3

Algum tempo atrás, escrevi uma format_datefunção, pois oferece muitas opções sobre como você deseja sua data :

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);
}    
Campeão
fonte
2
Esta resposta é tão errada quanto a resposta de khaldonno. Supõe (caso 10) que um ano tem 365 dias (a cada quarto ano tem 366 dias (exceto as regras de 100 anos / 400 anos para o calendário gregoriano)) e que um mês tem 30 dias (aproximadamente 30,42 dias) em anos não bissextos). Mesmo com constantes melhores, é correto apenas em média, não necessariamente correto para duas datas particulares.
Peter Mortensen
3

Muito simples:

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

Verifique o seguinte link para obter detalhes:

PHP: date_diff - Manual

Note que é para o PHP 5.3.0 ou superior.

Choudhury Saadmaan Mahmid
fonte
3

Uma função fácil

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

Use como

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

Retornará como 2 years 8 months 2 days

Anuj
fonte
3

Tente esta resposta muito simples usando date_diff () , isso é testado.

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

a saída é:

1 years 1 months 2 days
larp
fonte
2

Estou usando a seguinte função que eu escrevi, quando o PHP 5.3 (respectivamente date_diff ()) não está disponível:

        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
fonte
2

DateInterval é ótimo, mas tem algumas ressalvas:

  1. somente para PHP 5.3+ ( mas isso não é mais uma boa desculpa )
  2. suporta apenas anos, meses, dias, horas, minutos e segundos (sem semanas)
  3. calcula a diferença com todos os + dias acima (você não pode obter a diferença apenas em meses)

Para superar isso, codifiquei o seguinte (aprimorado da resposta @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;
}

Ele executa dois loops; o primeiro lida com os intervalos relativos (anos e meses) por força bruta, e o segundo calcula os intervalos absolutos adicionais com aritmética simples (por isso é mais rápido):

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
fonte
@ PeterMortensen: Deve funcionar, mas não dou garantias. Defina seu fuso horário e experimente.
Alix Axel