Как рассчитать разницу между двумя датами с помощью PHP?

722

У меня есть две даты в форме:

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

Теперь мне нужно найти разницу между этими двумя в следующей форме:

2 years, 3 months and 2 days

Как я могу сделать это в PHP?

Тарын
источник
1
2 года 94 дня. Расчет месяцев с учетом високосных лет будет проблематичным. Насколько точным это должно быть?
dbasnett
Возможный дубликат Как рассчитать относительное время?
Коул Джонсон

Ответы:

527

Используйте это для устаревшего кода (PHP <5.3). Для актуального решения см. Ответ Юрки ниже

Вы можете использовать strtotime (), чтобы преобразовать две даты в unix-время, а затем вычислить количество секунд между ними. Отсюда довольно легко рассчитать разные периоды времени.

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

Изменить: Очевидно, предпочтительный способ сделать это, как описано ниже Юркой. Мой код обычно рекомендуется, только если у вас нет PHP 5.3 или выше.

Несколько человек в комментариях указали, что приведенный выше код является лишь приблизительным. Я по-прежнему считаю, что для большинства целей это нормально, поскольку использование диапазона больше для того, чтобы дать представление о том, сколько времени прошло или осталось, а не для обеспечения точности - если вы хотите это сделать, просто выведите дату.

Несмотря на все это, я решил рассмотреть жалобы. Если вам действительно нужен точный диапазон, но у вас нет доступа к PHP 5.3, используйте приведенный ниже код (он также должен работать в PHP 4). Это прямой порт кода, который PHP использует внутренне для вычисления диапазонов, за исключением того, что он не учитывает переход на летнее время. Это означает, что он выключен максимум на час, но за исключением того, что это должно быть правильно.

<?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
Если вы используете класс DateTime, вы можете перейти к $ date-> format ('U'), чтобы получить метку времени Unix.
Джон Крам
4
Это неправда, если вам приходится иметь дело с летним / зимним временем. В этом конкретном случае, когда вы настраиваете летнее / зимнее время, один день равен 23 или 25 часам.
Арно
4
Ну, тот же аргумент может быть сделан в течение високосных лет. Это тоже не учитывается. Тем не менее, я не уверен, что вы даже хотите принять это во внимание, так как мы обсуждаем диапазон здесь. Семантика для диапазона несколько отличается от абсолютной даты.
Эмиль Х
9
Эта функция неверна. Это хорошо для приближения, но неверно для точных диапазонов. Например, предполагается, что в месяце 30 дней, то есть разница между днями с 1 февраля по 1 марта будет такой же, как и с 1 июля по 1 августа (независимо от високосного года).
enobrev
1
В PHP ссылочные переменные находятся в сигнатуре функции, а не в вызове. Переместите все свои &подписи.
Пол Тарьян
909

Я предлагаю использовать объекты DateTime и 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 ";

читать дальше php DateTime :: руководство по diff

Из руководства:

Начиная с PHP 5.2.2, объекты DateTime можно сравнивать с помощью операторов сравнения.

$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
источник
14
+1 DateTime правильно обрабатывает високосные годы и часовые пояса, и для полки есть хорошая книга: phparch.com/books/…
hakre
3
Есть ли метод, который дает общее количество секунд между двумя DateTimes? (без добавления компонентов, то есть)
potatoe
1
@Panique $ интервал-> дни и $ интервал-> d являются разными мерами. Ваш комментарий выше правильный "показывает общее количество дней (не разделенных на годы, месяцы и дни, как указано выше)"
Юрка
1
@potatoe Вы, вероятно, хотите $date2->format('U') - $date1->format('U').
Пауло Фрейтас
3
обратите внимание, что есть ошибка, когда DateInterval имеет неправильное свойство days ( всегда 6015 ) в Windows с некоторыми версиями PHP: bugs.php.net/bug.php?id=51184 (см. комментарии там для исправления / обходного решения)
Pim Schaaf
73

Лучший способ действий - использовать объекты PHP DateTimeDateInterval). Каждая дата инкапсулируется в DateTimeобъект, и тогда можно сделать различие между ними:

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

DateTimeОбъект будет принимать любой формат strtotime()будет. Если требуется еще более конкретный формат даты, DateTime::createFromFormat()его можно использовать для создания DateTimeобъекта.

После того, как оба объекта были созданы, вы отделяете один от другого с помощью DateTime::diff().

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

$differenceтеперь содержит DateIntervalобъект с информацией о разнице. А var_dump()выглядит так:

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

Чтобы отформатировать DateIntervalобъект, нам нужно проверить каждое значение и исключить его, если оно равно 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;
}

Осталось только вызвать нашу функцию на $difference DateIntervalобъекте:

echo format_interval($difference);

И мы получаем правильный результат:

20 дней 6 часов 56 минут 30 секунд

Полный код, используемый для достижения цели:

/**
 * 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);
Призрак Мадары
источник
DateTime()это не функция, это объект, и он существует с PHP 5.2. Убедитесь, что ваш сервер поддерживает это.
Призрак Мадары
2
@SecondRikudo DateTime :: Различаться нужно PHP 5.3.0
PhoneixS
У нас есть проблема, заменить first_date на second_date, и мы получаем тот же результат? Почему бы не сказать 0 дней 0 часов 0 минут 0 секунд или только 0. Пример: 2012-11-30 17:03:30 - 2012-12-21 00:00:00 и 2012-12-21 00:00:00 - 2012-11-30 17:03:30 получить тот же результат.
EgoistDeveloper
Потому что разница дает вам разницу между двумя разами. Разница не равна 0 независимо от того, какая дата наступит позже.
Призрак Мадары
1
Это действительно хороший ответ, поскольку он предоставляет понятную функцию, которая может быть вызвана из любой точки кодовой базы без большого количества временных вызовов. Другие ответы позволяют вам на лету отбрасывать повторяющиеся сообщения, которые касаются симптомов, а не решают проблему ... Единственный элемент, который я добавил (и почти все остальные сообщения не покрывают это), - это множественное число элементов $ interval если больше 1.
19
33

Посмотреть часы и минуты и секунды ..

$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
источник
7
Вероятно, это не даст точного результата.
Дельфин
8
И это ужасное решение, если только вы не вынуждены использовать ужасно устаревшую версию PHP ...
rdlowrey
2
Не так сухо . Например, 60 * 60 * 24 повторяется 15 раз. Да здравствует копирование-вставка!
Питер Мортенсен
Как насчет високосных лет? Год не в среднем 365 дней.
Питер Мортенсен
Этот код предполагает, что в среднем месяц составляет 30 дней. Даже если предположить, что год составляет 365 дней, средний месяц составляет 365/12 = 30,42 дня (приблизительно).
Питер Мортенсен
18

Взгляните на следующую ссылку. Это лучший ответ, который я нашел до сих пор .. :)

function dateDiff ($d1, $d2) {

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

} // end function dateDiff

Неважно, какая дата раньше или позже, когда вы передаете параметры даты. Функция использует абсолютное значение PHP ABS (), чтобы всегда возвращать положительное число как количество дней между двумя датами.

Имейте в виду, что количество дней между двумя датами НЕ включает обе даты. Поэтому, если вы ищете количество дней, представленное всеми датами между и включая введенные даты, вам нужно будет добавить один (1) к результату этой функции.

Например, разница (как показано вышеупомянутой функцией) между 2013-02-09 и 2013-02-14 составляет 5. Но количество дней или дат, представленных диапазоном дат 2013-02-09 - 2013-02- 14 - это 6.

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

casper123
источник
Вопрос задал разницу как количество лет, месяцев и дней, а не общее количество дней.
Питер Мортенсен
14

Я голосовал за Jurka «s ответ , как это мой любимый, но у меня есть пре-php.5.3 ...

Я обнаружил, что работаю над аналогичной проблемой - именно так я и пришел к этому вопросу, - но мне просто нужна была разница в часах. Но моя функция решила и эту проблему довольно хорошо, и у меня нет места в моей собственной библиотеке, где бы она не была потеряна и забыта, так что ... надеюсь, это кому-нибудь пригодится.

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

И тест:

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

И результат:

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

Я получил оригинальную идею отсюда , которую я модифицировал для своего использования (и я надеюсь, что моя модификация также появится на этой странице).

Вы можете очень легко удалить ненужные интервалы (скажем, «неделя»), удалив их из $aIntervalsмассива, добавив $aExcludeпараметр или просто отфильтровав их при выводе строки.

enobrev
источник
К сожалению, это не возвращает то же самое, что DateInterval из-за переполнения года / месяца.
Стивен Харрис
2
@StephenHarris: я не проверял это, но, читая код, я уверен, что он должен вернуть тот же результат - при условии, что вы удалите weekиндекс $aIntervals(так как DateDiffникогда его не используете).
Аликс Аксель
Это отличное решение для поиска дат, которые происходят каждый интервал между двумя датами.
промежуточный мозг
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
источник
Вопрос задал разницу как количество лет , месяцев и дней . Это выводит разницу как общее количество дней.
Питер Мортенсен
11

Я не знаю, используете ли вы PHP-фреймворк или нет, но во многих PHP-фреймворках есть библиотеки даты / времени и помощники, которые помогут вам не изобретать велосипед.

Например, CodeIgniter имеет timespan()функцию. Просто введите две метки времени Unix, и он автоматически сгенерирует такой результат:

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

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

Джейк Уилсон
источник
8

Пример использования:

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

Вывод :

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

Функция:

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';
}
Glavic
источник
если я хочу определить, больше ли разница, чем 30 минут, что мне делать?
Офир Аттиа
@OfirAttia: у вас есть куча таких вопросов здесь, на SO, просто используйте поиск. Простая демонстрация
Glavić
7

У меня есть простая логика для этого:

<?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;
      ?>
Хардик Равал
источник
Кажется, в конце примера кода чего-то не хватает (конечная скобка и " ?> "?).
Питер Мортенсен
«простая» логика. Это как минимум 40 строк чистого кода.
Маджос,
6

Вы можете использовать

getdate()

функция, которая возвращает массив, содержащий все элементы предоставленной даты / времени:

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

Если даты начала и окончания указаны в строковом формате, используйте

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

перед приведенным выше кодом

Марк Пим
источник
не похоже на работу. Я получаю дату в начале эры меток времени.
Сирбер
Важно понимать, что вам нужно сделать, $my_t["year"] -= 1970чтобы получить правильное количество лет. Кроме того, необходимо вычесть ваш час разница по Гринвичу , чтобы получить часы правильно. Вам также необходимо вычесть 1 из месяца и даты.
Салман А
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
источник
Учитывает ли "strtotime ('+ 1 year', $ date1)" високосные годы?
Питер Мортенсен
6

Это моя функция. Обязательный PHP> = 5.3.4. Он использует класс DateTime. Очень быстро, быстро и может сделать разницу между двумя датами или даже так называемым временем с тех пор.

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;
    }
}
Дэвид Беланджер
источник
6

Я бы предпочел использовать date_createи date_diffобъекты.

Код:

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

Вывод:

2 years, 3 months and 2 days

Для получения дополнительной информации прочитайте руководство по PHPdate_diff

Согласно руководству date_diffэто псевдоним DateTime :: diff ()

Adeel
источник
5

Это попытается определить, была ли указана временная метка или нет, а также вернет будущие даты / время в виде отрицательных значений:

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

Пример использования:

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

Или:

<?php
  $christmas_2020 = 'December 25, 2020';
  $countdown = time_diff($christmas_2020);
  print_r($countdown);
jerdiggity
источник
5

«если» дата хранится в MySQL, мне легче вычислить разницу на уровне базы данных ... Затем на основе выходных данных Day, Hour, Min, Sec, анализировать и отображать результаты соответствующим образом ...

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
источник
5

Я нашел вашу статью на следующей странице, которая содержит ряд ссылок на PHP вычисления даты и времени в .

Рассчитайте разницу между двумя датами (и временем), используя PHP. На следующей странице представлен ряд различных методов (всего 7) для выполнения вычислений даты / времени с использованием PHP для определения разницы во времени (часы, единицы), днях, месяцах или годах между двумя датами.

См. PHP Date Time - 7 методов для вычисления разницы между двумя датами .

Джеймс - Php Development
источник
4

Вы также можете использовать следующий код для возврата различий в датах с округлением до $ date1 = $ duedate; // назначить дату исполнения echo $ date2 = date ("Ymd"); // текущая дата $ ts1 = strtotime ($ date1); $ ts2 = strtotime ($ date2); $ seconds_diff = $ ts1 - $ ts2; echo $ datediff = ceil (($ seconds_diff / 3600) / 24); // возврат в днях

Если вы используете метод пола php вместо ceil, он вернет вам круглую дробь вниз. Пожалуйста, проверьте разницу здесь, иногда, если часовой пояс ваших промежуточных серверов отличается от часового пояса живого сайта, в этом случае вы можете получить другие результаты, поэтому измените условия соответствующим образом.

Рикин Адхьяпак
источник
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 ";
Лиза
источник
4

вы всегда можете использовать следующую функцию, которая может возвращать возраст в годах и месяцах (т.е. 1 год 4 месяца)

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

или если вы хотите, чтобы возраст был рассчитан на текущую дату, вы можете использовать

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

    return $years.'.'.months;
}
Рик Хармс
источник
4

Для версии php> = 5.3: создайте два объекта даты и затем используйте date_diff()функцию. Он вернет объект php DateInterval . см документацию

$date1=date_create("2007-03-24");
$date2=date_create("2009-06-26");
$diff=date_diff($date1,$date2);
echo $diff->format("%R%a days");
бикрам кц
источник
4

Вот исполняемый код

$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);
Мосин
источник
3

У меня была та же проблема с PHP 5.2 и я решил ее с MySQL. Может быть не совсем то, что вы ищете, но это поможет, и вернет количество дней:

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

Более подробная информация здесь http://dev.mysql.com/doc/refman/5.5/en/date-and-time-functions.html#function_datediff

Клемен Тушар
источник
3

Поскольку все публикуют примеры кода, вот еще одна версия.

Я хотел, чтобы функция отображала разницу от секунд до лет (всего одна единица). Для периодов более 1 дня я хотел, чтобы ролловер происходил в полночь (понедельник, 10:00, с 9:00, среда 2 дня назад, а не 1). И для периодов более месяца я хотел, чтобы ролловер происходил в один и тот же день месяца (включая 30/31 дневных месяцев и високосных лет).

Вот что я придумал:

/**
 * 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
источник
3

Некоторое время назад я написал format_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);
}    
чавканье
источник
2
Этот ответ так же неверен, как и ответ халдонно. Предполагается (случай 10), что в году 365 дней (каждый четвертый год имеет 366 дней (кроме правил 100 лет / 400 лет для григорианского календаря)) и что месяц имеет 30 дней (это примерно 30,42 дня в не високосные годы). Даже с лучшими константами это в среднем правильно, но не обязательно для любых двух конкретных дат.
Питер Мортенсен
3

Очень просто:

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

Пожалуйста, проверьте следующую ссылку для деталей:

PHP: date_diff - Руководство

Обратите внимание, что это для PHP 5.3.0 или выше.

Чоудхури Саадмаан Махмид
источник
3

Легкая функция

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

Используйте как

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

Вернется как 2 years 8 months 2 days

Ануй
источник
3

Попробуйте этот очень простой ответ, используя date_diff () , это проверено.

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

выход:

1 years 1 months 2 days
ПВЗП
источник
2

Я использую следующую функцию, которую я написал, когда PHP 5.3 (соответственно date_diff ()) недоступен:

        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);
        }
Константин Шегунов
источник
2

DateInterval это здорово, но есть пара предостережений:

  1. только для PHP 5.3+ ( но это больше не является хорошим оправданием )
  2. поддерживает только годы, месяцы, дни, часы, минуты и секунды (без недель)
  3. он вычисляет разницу со всеми вышеуказанными + днями (вы не можете получить разницу только по месяцам)

Чтобы преодолеть это, я написал следующее (улучшено по сравнению с ответом @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;
}

Он работает две петли; первый имеет дело с относительными интервалами (годами и месяцами) посредством грубой форсировки, а второй вычисляет дополнительные абсолютные интервалы с простой арифметикой (так что это быстрее):

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);
}
Аликс Аксель
источник
@PeterMortensen: Это должно работать, но я не даю никаких гарантий. Установите часовой пояс и попробуйте.
Аликс Аксель