Поиск количества дней между двумя датами

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

$now = time(); // or your date as well $your_date = strtotime("2010-01-01"); $datediff = $now - $your_date; echo floor($datediff / (60 * 60 * 24)); 

Если вы используете PHP 5.3 > , это, безусловно, самый точный способ вычисления разницы:

 $date1 = new DateTime("2010-07-06"); $date2 = new DateTime("2010-07-09"); $diff = $date2->diff($date1)->format("%a"); 

Преобразуйте даты в временные метки unix, затем выровняйте один из другого. Это даст вам разницу в секундах, которую вы разделите на 86400 (количество секунд в день), чтобы дать вам приблизительное количество дней в этом диапазоне.

Если ваши даты указаны в формате 25.1.2010 , 25.1.2010 или 2010-01-25 , вы можете использовать функцию strtotime :

 $start = strtotime('2010-01-25'); $end = strtotime('2010-02-20'); $days_between = ceil(abs($end - $start) / 86400); 

Используя ceil округляет количество дней до следующего полного дня. Вместо этого используйте floor если вы хотите получить количество полных дней между этими двумя датами.

Если ваши даты уже находятся в формате timestamp в формате unix, вы можете пропустить преобразование и просто выполнить $days_between . Для более экзотических форматов даты вам, возможно, придется выполнить какой-то пользовательский синтаксический анализ, чтобы получить правильное решение.

С PHP версии 5.3 и выше были добавлены новые функции даты / времени , чтобы получить разницу:

 $datetime1 = new DateTime("2010-06-20"); $datetime2 = new DateTime("2011-06-22"); $difference = $datetime1->diff($datetime2); echo 'Difference: '.$difference->y.' years, ' .$difference->m.' months, ' .$difference->d.' days'; print_r($difference); 

Результат, как показано ниже:

 Difference: 1 years, 0 months, 2 days DateInterval Object ( [y] => 1 [m] => 0 [d] => 2 [h] => 0 [i] => 0 [s] => 0 [invert] => 0 [days] => 367 ) 

Надеюсь, поможет !

TL; DR не используют отметки времени UNIX. Не используйте time() . Если вы это сделаете, будьте готовы, если ваша надежность 98.0825% не даст вам. Используйте DateTime (или Carbon).

Правильный ответ – тот, который дал Сакшам Гупта (другие ответы также верны):

 $date1 = new DateTime('2010-07-06'); $date2 = new DateTime('2010-07-09'); $days = $date2->diff($date1)->format('%a'); 

Или процедурно как однострочный:

 /** * Number of days between two dates. * * @param date $dt1 First date * @param date $dt2 Second date * @return int */ function daysBetween($dt1, $dt2) { return date_diff( date_create($dt2), date_create($dt1) )->format('%a'); } 

Если вы действительно должны использовать отметки времени UNIX, установите часовой пояс на GMT, чтобы избежать большинства ловушек, описанных ниже.


Длительный ответ

Большинство ответов с использованием временных меток UNIX делают два предположения, которые, вместе взятые, могут привести к сценариям с неправильными результатами и небольшими ошибками, которые могут быть трудно отследить, и возникают даже дни, недели или месяцы после успешного развертывания.

Первая ошибка не учитывает, что, когда его спросили: «Сколько прошло дней со вчерашнего дня?», Компьютер может честно ответить на ноль, если между настоящим и мгновением, указанным «вчера», прошло менее одного дня .

Обычно при преобразовании «дня» в временную метку UNIX получается временная метка в полночь того дня.

Таким образом, между полуночами с 1 октября по 15 октября прошло пятнадцать дней. Но с 13:00 1 октября и 14:55 15 октября прошло пятнадцать дней минус 5 минут , и большинство решений, использующих floor() или выполняющих неявное целочисленное преобразование , сообщают на один день меньше, чем ожидалось .

Итак, «сколько дней назад был Ymd H: i: s»? даст неправильный ответ .

Вторая ошибка – один день – 86400 секунд. Это почти всегда верно – это бывает достаточно часто, чтобы не заметить время, когда этого не происходит. Но расстояние в секундах между двумя последовательными полуночами – это не 86400, по крайней мере, два раза в год, когда вступает в действие летнее время. Сравнение двух дат на границе DST снова даст неправильный ответ.

Поэтому, даже если вы используете «взломать» форсирование всех временных дат даты до фиксированного часа, скажем, в полночь (это также делается неявно на разных языках и фреймах, когда вы указываете только день-месяц-год, а не час-минута-секунда) , широко используемая формула

  (unix_timestamp(DATE2) - unix_timestamp(DATE1)) / 86400 

или пол (время () – strtotime ($ somedate)) / 86400

вернется, скажем, 17, когда DATE1 и DATE2 находятся в одном сегменте DST года; но он может вернуться 17.042, а еще хуже – 16.958. Использование floor () или любого неявного усечения в integer затем преобразует то, что должно было быть от 17 до 16.

И все становится еще более уродливым, поскольку такой код не переносится на разных платформах, потому что некоторые из них могут применять секунды прыжка, а некоторые – нет . На тех платформах, которые делают , разница между двумя датами не будет 86400, а 86401, или, может быть, 86399. Таким образом, код, который работал в мае и фактически прошел все тесты, прорвется в следующем июне, когда 1299999 дней считаются 12 днями вместо 13. Две даты который работал в 2015 году, не будет работать в 2017 году – те же даты, и ни один год не является високосным годом. Но между 2018-01-01 и 2017-01-01 на тех платформах, которые заботятся, 366 дней пройдут вместо 365, сделав 2017 год високосным.

Поэтому, если вы действительно хотите использовать временные метки UNIX:

  • используйте функцию round() , а не floor() .

  • в качестве альтернативы, не рассчитывайте разницы между D1-M1-YYY1 и D2-M2-YYY2. Эти даты будут действительно рассматриваться как D1-M1-YYY1 00:00:00 и D2-M2-YYY2 00:00:00. Скорее, конвертируйте между D1-M1-YYY1 22:30:00 и D2-M2-YYY2 04:30:00. Вы всегда получите остаток около двадцати часов. Это может стать двадцать один час или девятнадцать, а может быть, восемнадцать часов, пятьдесят девять минут тридцать шесть секунд. Не важно. Это большой запас, который останется там и останется положительным в обозримом будущем. Теперь вы можете усекать его с помощью floor() в безопасности.

Правильное решение состоит в том, чтобы

  • использовать библиотеку времени (Datetime, Carbon, что угодно); не сворачивайте свои собственные

  • пишите всесторонние тестовые примеры, используя действительно злые даты – через границы DST, через високосные годы, через прыжки секунд и т. д., а также банальные даты. В идеале (призывы к дате времени бывают быстрыми !) Генерируют четыре года (и один день) даты, собирая их из строк, последовательно и гарантируя, что разница между первым днем ​​и днем, прошедшим тестирование, неуклонно растет. Это гарантирует, что, если что-либо изменится в низкоуровневых процедурах и секундах прыжков, попытайтесь нанести ущерб, по крайней мере, вы узнаете .

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


Независимо от вашего решения, проверьте его

Функция funcdiff ниже реализует одно из решений (как это принято, принятое) в реальном мире.

 <?php $tz = 'Europe/Rome'; $yearFrom = 1980; $yearTo = 2020; $verbose = false; function funcdiff($date2, $date1) { $now = strtotime($date2); $your_date = strtotime($date1); $datediff = $now - $your_date; return floor($datediff / (60 * 60 * 24)); } ######################################## date_default_timezone_set($tz); $failures = 0; $tests = 0; $dom = array ( 0, 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 ); array_sum($dom) === 365 || die("Thirty days hath November..."); $last = array(); for ($year = $yearFrom; $year < $yearTo; $year++) { $dom[2] = 28; if ($year % 4 === 0) { $dom[2] = 29; } if ($year % 100 === 0) { $dom[2] = 28; } if ($year % 400 === 0) { $dom[2] = 29; } for ($month = 1; $month <= 12; $month ++) { for ($day = 1; $day <= $dom[$month]; $day++) { $date = sprintf("%04d-%02d-%02d", $year, $month, $day); if (count($last) === 7) { $tests ++; $diff = funcdiff($date, $test = array_shift($last)); if ((double)$diff !== (double)7) { $failures ++; if ($verbose) { print "There seem to be {$diff} days between {$date} and {$test}\n"; } } } $last[] = $date; } } } print "This function failed {$failures} of its {$tests} tests between {$yearFrom} and {$yearTo}.\n"; 

В результате,

 This function failed 280 of its 14603 tests 

История ужасов: стоимость «экономии времени»

Это произошло несколько месяцев назад. Гениальный программист решил сэкономить несколько микросекунд при расчете, которое заняло не более тридцати секунд, подключив печально известный код «(MidnightOfDateB-MidnightOfDateA) / 86400» в нескольких местах. Это была настолько очевидная оптимизация, что он даже не документировал ее, и оптимизация прошла интеграционные тесты и скрывалась в коде несколько месяцев, и все это незамечено.

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

Бесконечно худшие, они потеряли (уже очень мало) вера, которую они имели в программе, не предназначенную для тайного приведения их в порядок, и притворились – и получили – полный подробный обзор кода с тестовыми примерами был запущен и прокомментирован в терминах непрофессионала (плюс много лечения красной ковровой дорожкой в ​​следующие недели).

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

На минусовой стороне вся броухаха стоила компании около 200 000 евро впереди – плюс лицо, плюс, несомненно, какая-то способность торговаться (и, следовательно, еще больше денег).

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

Девяносто девять раз из ста, «взлом 86400» будет работать безупречно. (Например, в PHP, strtotime() игнорирует DST и сообщает, что между полуночи последней субботы октября и дня следующего понедельника прошло ровно 2 * 24 * 60 * 60 секунд, даже если это явно не так … и две неудачи с радостью сделают одно право).

Это, дамы и господа, был одним из примеров, когда этого не произошло. Как и в случае с подушками безопасности и ремнями безопасности, вам, возможно, никогда не понадобится сложность (и простота использования) DateTime или Carbon . Но день, когда вы (или день, когда вам придется доказывать, что вы об этом подумали), придет как вор ночью. Приготовься.

Простота использования date_diff

 $from=date_create(date('Ym-d')); $to=date_create("2013-03-15"); $diff=date_diff($to,$from); print_r($diff); echo $diff->format('%R%a days'); 

Объектно-ориентированный стиль:

 $datetime1 = new DateTime('2009-10-11'); $datetime2 = new DateTime('2009-10-13'); $interval = $datetime1->diff($datetime2); echo $interval->format('%R%a days'); 

Процедурный стиль:

 $datetime1 = date_create('2009-10-11'); $datetime2 = date_create('2009-10-13'); $interval = date_diff($datetime1, $datetime2); echo $interval->format('%R%a days'); 

Ну, выбранный ответ не самый правильный, потому что он выйдет за пределы UTC. В зависимости от часового пояса ( списка ) могут быть корректировки времени, создающие дни «без» 24 часов, и это приведет к сбою расчета (60 * 60 * 24).

Вот пример:

 date_default_timezone_set('europe/lisbon'); $time1 = strtotime('2016-03-27'); $time2 = strtotime('2016-03-29'); echo floor( ($time2-$time1) /(60*60*24)); ^-- the output will be **1** 

Поэтому правильным решением будет использование DateTime

 date_default_timezone_set('europe/lisbon'); $date1 = new DateTime("2016-03-27"); $date2 = new DateTime("2016-03-29"); echo $date2->diff($date1)->format("%a"); ^-- the output will be **2** 

Использовал это 🙂

 $days = (strtotime($endDate) - strtotime($startDate)) / (60 * 60 * 24); print $days; 

Теперь он работает

 $start = '2013-09-08'; $end = '2013-09-15'; $diff = (strtotime($end)- strtotime($start))/24/3600; echo $diff; 

Я использую Carbon в моих проектах композитора для этой и подобных целей.

Это было бы так просто:

 $dt = Carbon::parse('2010-01-01'); echo $dt->diffInDays(Carbon::now()); 
 $datediff = floor(strtotime($date1)/(60*60*24)) - floor(strtotime($date2)/(60*60*24)); 

и, при необходимости:

 $datediff=abs($datediff); 

Если у вас есть время в секундах (отметка времени Unix Unix), вы можете просто вычесть время и разделить на 86400 (секунды в день)

 function howManyDays($startDate,$endDate) { $date1 = strtotime($startDate." 0:00:00"); $date2 = strtotime($endDate." 23:59:59"); $res = (int)(($date2-$date1)/86400); return $res; } 

Если вы хотите повторить все дни между датой начала и окончания, я придумал следующее:

 $startdatum = $_POST['start']; // starting date $einddatum = $_POST['eind']; // end date $now = strtotime($startdatum); $your_date = strtotime($einddatum); $datediff = $your_date - $now; $number = floor($datediff/(60*60*24)); for($i=0;$i <= $number; $i++) { echo date('dmY' ,strtotime("+".$i." day"))."<br>"; } 
  // Change this to the day in the future $day = 15; // Change this to the month in the future $month = 11; // Change this to the year in the future $year = 2012; // $days is the number of days between now and the date in the future $days = (int)((mktime (0,0,0,$month,$day,$year) - time(void))/86400); echo "There are $days days until $day/$month/$year"; 

Если вы используете MySql

 function daysSince($date, $date2){ $q = "SELECT DATEDIFF('$date','$date2') AS days;"; $result = execQ($q); $row = mysql_fetch_array($result,MYSQL_BOTH); return ($row[0]); 

}

 function execQ($q){ $result = mysql_query( $q); if(!$result){echo ('Database error execQ' . mysql_error());echo $q;} return $result; 

}

Вот моя улучшенная версия, которая показывает 1 год (ы) 2 Месяцев (ы) 25 дней (ы), если 2-й параметр передан.

 class App_Sandbox_String_Util { /** * Usage: App_Sandbox_String_Util::getDateDiff(); * @param int $your_date timestamp * @param bool $hr human readable. eg 1 year(s) 2 day(s) * @see http://stackoverflow.com/questions/2040560/finding-the-number-of-days-between-two-dates * @see http://qSandbox.com */ static public function getDateDiff($your_date, $hr = 0) { $now = time(); // or your date as well $datediff = $now - $your_date; $days = floor( $datediff / ( 3600 * 24 ) ); $label = ''; if ($hr) { if ($days >= 365) { // over a year $years = floor($days / 365); $label .= $years . ' Year(s)'; $days -= 365 * $years; } if ($days) { $months = floor( $days / 30 ); $label .= ' ' . $months . ' Month(s)'; $days -= 30 * $months; } if ($days) { $label .= ' ' . $days . ' day(s)'; } } else { $label = $days; } return $label; } } 

Попробуйте использовать углерод

 $d1 = \Carbon\Carbon::now()->subDays(92); $d2 = \Carbon\Carbon::now()->subDays(10); $days_btw = $d1->diffInDays($d2); 

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

 \Carbon\Carbon::parse('') 

для создания объекта Carbon date с использованием заданной строки timestamp.

 $early_start_date = date2sql($_POST['early_leave_date']); $date = new DateTime($early_start_date); $date->modify('+1 day'); $date_a = new DateTime($early_start_date . ' ' . $_POST['start_hr'] . ':' . $_POST['start_mm']); $date_b = new DateTime($date->format('Ym-d') . ' ' . $_POST['end_hr'] . ':' . $_POST['end_mm']); $interval = date_diff($date_a, $date_b); $time = $interval->format('%h:%i'); $parsed = date_parse($time); $seconds = $parsed['hour'] * 3600 + $parsed['minute'] * 60; // display_error($seconds); $second3 = $employee_information['shift'] * 60 * 60; if ($second3 < $seconds) display_error(_('Leave time can not be greater than shift time.Please try again........')); set_focus('start_hr'); set_focus('end_hr'); return FALSE; } 
 <?php $date1=date_create("2013-03-15"); $date2=date_create("2013-12-12"); $diff=date_diff($date1,$date2); echo $diff->format("%R%a days"); ?> 

использовал вышеуказанный код очень просто. Благодарю.

Это работает!

 $start = strtotime('2010-01-25'); $end = strtotime('2010-02-20'); $days_between = ceil(abs($end - $start) / 86400);