Я хочу подсчитать общую дневную разницу с пользовательским вводом
Например, когда пользователь вводит
start_date = 2012-09-06
и end-date = 2012-09-11
Пока я использую этот код, чтобы найти разницу
$count = abs(strtotime($start_date) - strtotime($end_date)); $day = $count+86400; $total = floor($day/(60*60*24));
Итогом будет 6. Но проблема в том, что я не хочу включать дни в выходные (субботу и воскресенье)
2012-09-06 2012-09-07 2012-09-08 Saturday 2012-09-09 Sunday 2012-09-10 2012-09-11
Таким образом, результат будет 4
—-Обновить—
У меня есть таблица, содержащая дату, название таблицы – дата отпуска
например, таблица содержит 2012-09-07
Таким образом, общий день будет 3, потому что он не учитывает дату праздника
как это сделать, чтобы приравнивать дату от ввода до даты в таблице?
Очень легко с моими фаворитами: DateTime
, DateInterval
и DatePeriod
$start = new DateTime('2012-09-06'); $end = new DateTime('2012-09-11'); // otherwise the end date is excluded (bug?) $end->modify('+1 day'); $interval = $end->diff($start); // total days $days = $interval->days; // create an iterateable period of date (P1D equates to 1 day) $period = new DatePeriod($start, new DateInterval('P1D'), $end); // best stored as array, so you can add more than one $holidays = array('2012-09-07'); foreach($period as $dt) { $curr = $dt->format('D'); // substract if Saturday or Sunday if ($curr == 'Sat' || $curr == 'Sun') { $days--; } // (optional) for the updated question elseif (in_array($dt->format('Ym-d'), $holidays)) { $days--; } } echo $days; // 4
В моем случае мне нужен был тот же ответ, что и у OP, но хотелось что-то немного меньше. @ Ответ Бояна сработал, но мне не понравилось, что он не работает с объектами DateTime
, требуемый с использованием временных меток, и сравнивал его с strings
а не с самими реальными объектами (которые чувствуют себя взломанными) … Вот пересмотренная версия его ответ.
function getWeekdayDifference(\DateTime $startDate, \DateTime $endDate) { $days = 0; while($startDate->diff($endDate)->days > 0) { $days += $startDate->format('N') < 6 ? 1 : 0; $startDate = $startDate->add(new \DateInterval("P1D")); } return $days; }
Комментарий Per @ xzdead, если вы хотите, чтобы это включало дату начала и окончания:
function getWeekdayDifference(\DateTime $startDate, \DateTime $endDate) { $isWeekday = function (\DateTime $date) { return $date->format('N') < 6; }; $days = $isWeekday($endDate) ? 1 : 0; while($startDate->diff($endDate)->days > 0) { $days += $isWeekday($startDate) ? 1 : 0; $startDate = $startDate->add(new \DateInterval("P1D")); } return $days; }
дата («N») получает день недели (1 – понедельник, 7 – воскресенье)
$start = strtotime('2012-08-06'); $end = strtotime('2012-09-06'); $count = 0; while(date('Ym-d', $start) < date('Ym-d', $end)){ $count += date('N', $start) < 6 ? 1 : 0; $start = strtotime("+1 day", $start); } echo $count;
использовать DateTime
:
$datetime1 = new DateTime('2012-09-06'); $datetime2 = new DateTime('2012-09-11'); $interval = $datetime1->diff($datetime2); $woweekends = 0; for($i=0; $i<=$interval->d; $i++){ $modif = $datetime1->modify('+1 day'); $weekday = $datetime1->format('w'); if($weekday != 0 && $weekday != 6){ // 0 for Sunday and 6 for Saturday $woweekends++; } } echo $woweekends." days without weekend"; // 4 days without weekends
Самый простой и быстрый способ получить разницу без выходных – это использовать библиотеку Carbon .
Вот пример использования:
<?php $from = Carbon\Carbon::parse('2016-05-21 22:00:00'); $to = Carbon\Carbon::parse('2016-05-21 22:00:00'); echo $to->diffInWeekdays($from);
Взгляните на этот пост: Рассчитать рабочие дни
(В вашем случае вы можете оставить часть «праздников» с тех пор, как вы работаете только с рабочими днями)
<?php //The function returns the no. of business days between two dates function getWorkingDays($startDate,$endDate){ // do strtotime calculations just once $endDate = strtotime($endDate); $startDate = strtotime($startDate); //The total number of days between the two dates. We compute the no. of seconds and divide it to 60*60*24 //We add one to inlude both dates in the interval. $days = ($endDate - $startDate) / 86400 + 1; $no_full_weeks = floor($days / 7); $no_remaining_days = fmod($days, 7); //It will return 1 if it's Monday,.. ,7 for Sunday $the_first_day_of_week = date("N", $startDate); $the_last_day_of_week = date("N", $endDate); //---->The two can be equal in leap years when february has 29 days, the equal sign is added here //In the first case the whole interval is within a week, in the second case the interval falls in two weeks. if ($the_first_day_of_week <= $the_last_day_of_week) { if ($the_first_day_of_week <= 6 && 6 <= $the_last_day_of_week) $no_remaining_days--; if ($the_first_day_of_week <= 7 && 7 <= $the_last_day_of_week) $no_remaining_days--; } else { // (edit by Tokes to fix an edge case where the start day was a Sunday // and the end day was NOT a Saturday) // the day of the week for start is later than the day of the week for end if ($the_first_day_of_week == 7) { // if the start date is a Sunday, then we definitely subtract 1 day $no_remaining_days--; if ($the_last_day_of_week == 6) { // if the end date is a Saturday, then we subtract another day $no_remaining_days--; } } else { // the start date was a Saturday (or earlier), and the end date was (Mon..Fri) // so we skip an entire weekend and subtract 2 days $no_remaining_days -= 2; } } //The no. of business days is: (number of weeks between the two dates) * (5 working days) + the remainder //---->february in none leap years gave a remainder of 0 but still calculated weekends between first and last day, this is one way to fix it $workingDays = $no_full_weeks * 5; if ($no_remaining_days > 0 ) { $workingDays += $no_remaining_days; } return $workingDays; } // This will return 4 echo getWorkingDays("2012-09-06","2012-09-11"); ?>
Если вам не нужны полные дни, но точны секунды, попробуйте этот код. Это принимает временные метки unix в качестве входных данных.
function timeDifferenceWithoutWeekends($from, $to) { $start = new DateTime("@".$from); $current = clone $start; $end = new DateTime("@".$to); $sum = 0; while ($current<$end) { $endSlice = clone $current; $endSlice->setTime(0,0,0); $endSlice->modify('+1 day'); if ($endSlice>$end) { $endSlice= clone $end; } $seconds = $endSlice->getTimestamp()-$current->getTimestamp(); $currentDay = $current->format("D"); if ($currentDay != 'Sat' && $currentDay != 'Sun') { $sum+=$seconds; } $current = $endSlice; } return $sum; }
/** * Getting the Weekdays count[ Excludes : Weekends] * * @param type $fromDateTimestamp * @param type $toDateTimestamp * @return int */ public static function getWeekDaysCount($fromDateTimestamp = null, $toDateTimestamp=null) { $startDateString = date('Ym-d', $fromDateTimestamp); $timestampTomorrow = strtotime('+1 day', $toDateTimestamp); $endDateString = date("Ymd", $timestampTomorrow); $objStartDate = new \DateTime($startDateString); //intialize start date $objEndDate = new \DateTime($endDateString); //initialize end date $interval = new \DateInterval('P1D'); // set the interval as 1 day $dateRange = new \DatePeriod($objStartDate, $interval, $objEndDate); $count = 0; foreach ($dateRange as $eachDate) { if ( $eachDate->format("w") != 6 && $eachDate->format("w") != 0 ) { ++$count; } } return $count; }
Вот альтернатива для расчета рабочих дней между двумя датами, а также исключает каникулы в США с использованием Pear's Date_Holidays от http://pear.php.net/package/Date_Holidays .
$ start_date и $ end_date должны быть объектами DateTime (вы можете использовать new DateTime('@'.$timestamp)
для преобразования из временной метки в объект DateTime).
<?php function business_days($start_date, $end_date) { require_once 'Date/Holidays.php'; $dholidays = &Date_Holidays::factory('USA'); $days = 0; $period = new DatePeriod($start_date, new DateInterval('P1D'), $end_date); foreach($period as $dt) { $curr = $dt->format('D'); if($curr != 'Sat' && $curr != 'Sun' && !$dholidays->isHoliday($dt->format('Ym-d'))) { $days++; } } return $days; } ?>