У меня есть две даты в форме:
Start Date: 2007-03-24
End Date: 2009-06-26
Теперь мне нужно найти разницу между этими двумя в следующей форме:
2 years, 3 months and 2 days
Как я могу сделать это в PHP?
Для 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)));
Я предлагаю использовать объекты 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)
Лучший курс действий — использование 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
объект с информацией о разнице. 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);
Посмотреть часы и минуты и секунды ..
$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);
Взгляните на следующую ссылку. Это лучший ответ, который я нашел до сих пор .. 🙂
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.
Я голосовал за Jurka«s ответ как это мой любимый, но у меня есть версия 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
параметр или просто отфильтровать их при выводе строки.
Я не знаю, используете ли вы PHP-фреймворк или нет, но многие PHP-фреймворки имеют библиотеки даты / времени и помощников, которые помогут вам не изобретать велосипед.
Например, CodeIgniter имеет timespan()
функция. Просто введите две метки времени Unix, и он автоматически сгенерирует такой результат:
1 Year, 10 Months, 2 Weeks, 5 Days, 10 Hours, 16 Minutes
<?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);
?>