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

632

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

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

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

2 years, 3 months and 2 days

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

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

31 ответ

448
Лучший ответ

Для 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 часам.
Показать ещё 5 комментариев
733

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

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

Начиная с 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)
  • 109
    обратите внимание, что DateTime-> diff () является php 5.3+
  • 14
    +1 DateTime правильно обрабатывает високосные годы и часовые пояса, и для полки есть хорошая книга: phparch.com/books/…
Показать ещё 13 комментариев
56

Лучший способ действия - использовать PHP DateTime DateInterval). Каждая дата инкапсулируется в объект 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 с информацией о различиях. A 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);
  • 0
    DateTime () не работает на моем PHP-сервере
  • 0
    DateTime() - это не функция, это объект, и он существует с PHP 5.2. Убедитесь, что ваш сервер поддерживает это.
Показать ещё 4 комментария
35

Просмотр часов, минут и секунд.

$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); 
  • 7
    Вероятно, это не даст точного результата.
  • 8
    И это ужасное решение, если только вы не вынуждены использовать ужасно устаревшую версию PHP ...
Показать ещё 5 комментариев
14

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

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

  • 0
    В вопросе задавалась разница как количество лет, месяцев и дней, а не общее количество дней.
12

Я проголосовал за jurka ответ как мой любимый, но у меня есть pre-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 или просто отфильтровывая их при выводе строки.

  • 0
    К сожалению, это не возвращает то же самое, что DateInterval из-за переполнения года / месяца.
  • 2
    @StephenHarris: я не проверял это, но, читая код, я уверен, что он должен вернуть тот же результат - при условии, что вы удаляете индекс week в $aIntervals (поскольку DateDiff никогда не использует его).
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

10
<?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);
?>
  • 0
    Вопрос задал разницу как количество лет , месяцев и дней . Это выводит разницу как общее количество дней.
8

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

$date1 = new DateTime($date_1);
$date2 = new DateTime($date_2);
$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 ";
6

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

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

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

  • 0
    не похоже на работу. Я получаю дату в начале эры меток времени.
  • 0
    Важно понимать, что вам нужно сделать $my_t["year"] -= 1970 чтобы получить правильное количество лет. Кроме того, необходимо вычесть час разница по Гринвичу , чтобы получить часы правильно. Вам также необходимо вычесть 1 из месяца и даты.
5

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

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

Это моя функция. Требуется 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;
    }
}
5
// 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;
}
  • 0
    Учитывает ли "strtotime ('+ 1 year', $ date1)" високосные годы?
4

"если" дата хранится в MySQL, мне легче выполнить разностный расчет на уровне базы данных... Затем, основываясь на выходе "День", "Час", "Мин", "Секундомер", проанализируйте и отобразите результаты соответственно..

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

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

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

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

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

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

3

Для версии 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");
3

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

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

2

вы всегда можете использовать следующую функцию, которая может вернуть возраст в годах и месяцах (т.е. 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;
}
2

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

    <?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 или выше.

2

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

Мне нужна функция для отображения различий от секунд до нескольких лет (всего одна единица). Для периодов более 1 дня я хотел, чтобы он опрокинулся в полночь (10 утра в понедельник, видно с 9 утра в среду, 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';
}
2

У меня была та же проблема с 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

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

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

function time_difference ($time_1, $time_2) {   

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

    $interval = $val_1->diff($val_2);
    $year     = $interval->y;
    $month    = $interval->m;
    $day      = $interval->d;
    $hour     = $interval->h;
    $minute   = $interval->i;
    $second   = $interval->s;

    $output   = '';

    if($year > 0){
        if ($year > 1){
            $output .= $year." years ";     
        } else {
            $output .= $year." year ";
        }
    }

    if($month > 0){
        if ($month > 1){
            $output .= $month." months ";       
        } else {
            $output .= $month." month ";
        }
    }

    if($day > 0){
        if ($day > 1){
            $output .= $day." days ";       
        } else {
            $output .= $day." day ";
        }
    }

    if($hour > 0){
        if ($hour > 1){
            $output .= $hour." hours ";     
        } else {
            $output .= $hour." hour ";
        }
    }

    if($minute > 0){
        if ($minute > 1){
            $output .= $minute." minutes ";     
        } else {
            $output .= $minute." minute ";
        }
    }

    if($second > 0){
        if ($second > 1){
            $output .= $second." seconds";      
        } else {
            $output .= $second." second";
        }
    }

    return $output;
}

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

echo time_difference ($time_1, $time_2);

2

Некоторое время назад я написал функцию 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 дня в не високосные годы). Даже с лучшими константами это только в среднем правильно, не обязательно правильно для любых двух конкретных дат.
1

В для копейки, за фунт: Я только что рассмотрел несколько решений, все из которых предлагают комплексное решение с использованием floor(), которое затем округляется до 26-летнего 12-месячного и 2-дневного решения, для чего должно было быть 25 лет, 11 месяцев и 20 дней!!!!

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

//задайте даты ввода/переменные::

$ISOstartDate   = "1987-06-22";
$ISOtodaysDate = "2013-06-22";

//Нам нужно ОБРАТИТЬ формат ISO yyyy-mm-dd в yyyy mm dd, как показано ниже:

$yDate [] = explode ('-', $ISOstartDate);   print_r ($ yDate);

$zDate [] = explode ('-', $ISOtodaysDate);   print_r ($ zDate);

// Lets Sort of the Years!
// Lets Sort out the difference in YEARS between startDate and todaysDate ::
$years = $zDate[0][0] - $yDate[0][0];

// We need to collaborate if the month = month = 0, is before or after the Years Anniversary ie 11 months 22 days or 0 months 10 days...
if ($months == 0 and $zDate[0][1] > $ydate[0][1]) {
    $years = $years -1;
}
// TEST result
echo "\nCurrent years => ".$years;

// Lets Sort out the difference in MONTHS between startDate and todaysDate ::
$months = $zDate[0][1] - $yDate[0][1];

// TEST result
echo "\nCurrent months => ".$months;

// Now how many DAYS has there been - this assumes that there is NO LEAP years, so the calculation is APPROXIMATE not 100%
// Lets cross reference the startDates Month = how many days are there in each month IF m-m = 0 which is a years anniversary
// We will use a switch to check the number of days between each month so we can calculate days before and after the years anniversary

switch ($yDate[0][1]){
    case 01:    $monthDays = '31';  break;  // Jan
    case 02:    $monthDays = '28';  break;  // Feb
    case 03:    $monthDays = '31';  break;  // Mar
    case 04:    $monthDays = '30';  break;  // Apr
    case 05:    $monthDays = '31';  break;  // May
    case 06:    $monthDays = '30';  break;  // Jun
    case 07:    $monthDays = '31';  break;  // Jul
    case 08:    $monthDays = '31';  break;  // Aug
    case 09:    $monthDays = '30';  break;  // Sept
    case 10:    $monthDays = '31';  break;  // Oct
    case 11:    $monthDays = '30';  break;  // Nov
    case 12:    $monthDays = '31';  break;  // Dec
};
// TEST return
echo "\nDays in start month ".$yDate[0][1]." => ".$monthDays;


// Lets correct the problem with 0 Months - is it 11 months + days, or 0 months +days???

$days = $zDate[0][2] - $yDate[0][2] +$monthDays;
echo "\nCurrent days => ".$days."\n";

// Lets now Correct the months to being either 11 or 0 Months, depending upon being + or - the years Anniversary date 
// At the same time build in error correction for Anniversary dates not being 1yr 0m 31d... see if ($days == $monthDays )
if($days < $monthDays && $months == 0)
    {
    $months = 11;       // If Before the years anniversary date
    }
else    {
    $months = 0;        // If After the years anniversary date
    $years = $years+1;  // Add +1 to year
    $days = $days-$monthDays;   // Need to correct days to how many days after anniversary date
    };
// Day correction for Anniversary dates
if ($days == $monthDays )   // if todays date = the Anniversary DATE! set days to ZERO
    {
    $days = 0;          // days set toZERO so 1 years 0 months 0 days
    };

    echo "\nTherefore, the number of years/ months/ days/ \nbetween start and todays date::\n\n";

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

конечный результат: 26 лет, 0 месяцев, 0 дней

Как долго я был в бизнесе 22 июня 2013 года - Ouch!

1

DateInterval отлично, но у него есть несколько предостережений:

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

Чтобы преодолеть это, я закодировал следующее (улучшенное из @enobrev answer):

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);
}
  • 0
    Он не поддерживает напрямую неделю, потому что нет необходимости. 7 дней это неделя ...
  • 1
    Работает ли это на високосные годы ?
Показать ещё 1 комментарий
1

Я использую следующую функцию, которую я написал, когда 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);
        }
0

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

Подробнее читайте PHP date_diff руководство

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

0
$date = '2012.11.13';
$dateOfReturn = '2017.10.31';

$substract = str_replace('.', '-', $date);

$substract2 = str_replace('.', '-', $dateOfReturn);



$date1 = $substract;
$date2 = $substract2;

$ts1 = strtotime($date1);
$ts2 = strtotime($date2);

$year1 = date('Y', $ts1);
$year2 = date('Y', $ts2);

$month1 = date('m', $ts1);
$month2 = date('m', $ts2);

echo $diff = (($year2 - $year1) * 12) + ($month2 - $month1);

Ещё вопросы

Сообщество Overcoder
Наверх
Меню