Разница между двумя датами в минутах, исключая выходные и праздничные дни
Я новичок в программировании на PHP и столкнулся с интересной проблемой. Я пробовал несколько поисков и нашел разные решения, но ни один из них не подходит под мою точную проблему.
У меня есть 2 даты в формате MySQL (например, 2014-04-10 09:00:00). Мне нужно знать разницу в минутах между этими двумя временными метками, но я должен исключить часы отсутствия на работе, выходные и праздничные дни.
Например, метка времени сегодня (2014-04-11 14:00:00) и метка времени понедельника (2014-04-14 11:00:00) должны отображаться в результате за 390 минут (рабочие дни с 08:30 до 18:00).).
Все решения на stackexchange показывают результаты в виде часов или дней, но мне нужна большая точность.
Заранее спасибо и извинения есть что-то непонятное.
3 ответа
Пример использования:
$from = '2013-09-06 15:45:32';
$to = '2013-09-14 21:00:00';
echo some_func_name($from, $to);
Выход:
1 day, 22 hours, 14 minutes, 28 seconds
Функция:
function some_func_name($from, $to) {
$workingDays = [1, 2, 3, 4, 5]; # date format = N
$workingHours = ['from' => ['08', '00'], 'to' => ['17', '00']];
$start = new DateTime($from);
$end = new DateTime($to);
$startP = clone $start;
$startP->setTime(0, 0, 0);
$endP = clone $end;
$endP->setTime(23, 59, 59);
$interval = new DateInterval('P1D');
$periods = new DatePeriod($startP, $interval, $endP);
$sum = [];
foreach ($periods as $i => $period) {
if (!in_array($period->format('N'), $workingDays)) continue;
$startT = clone $period;
$startT->setTime($workingHours['from'][0], $workingHours['from'][1]);
if (!$i && $start->diff($startT)->invert) $startT = $start;
$endT = clone $period;
$endT->setTime($workingHours['to'][0], $workingHours['to'][1]);
if (!$end->diff($endT)->invert) $endT = $end;
#echo $startT->format('Y-m-d H:i') . ' - ' . $endT->format('Y-m-d H:i') . "\n"; # debug
$diff = $startT->diff($endT);
if ($diff->invert) continue;
foreach ($diff as $k => $v) {
if (!isset($sum[$k])) $sum[$k] = 0;
$sum[$k] += $v;
}
}
if (!$sum) return 'ccc, no time on job?';
$spec = "P{$sum['y']}Y{$sum['m']}M{$sum['d']}DT{$sum['h']}H{$sum['i']}M{$sum['s']}S";
$interval = new DateInterval($spec);
$startS = new DateTime;
$endS = clone $startS;
$endS->sub($interval);
$diff = $endS->diff($startS);
$labels = [
'y' => 'year',
'm' => 'month',
'd' => 'day',
'h' => 'hour',
'i' => 'minute',
's' => 'second',
];
$return = [];
foreach ($labels as $k => $v) {
if ($diff->$k) {
$return[] = $diff->$k . ' ' . $v . ($diff->$k > 1 ? 's' : '');
}
}
return implode(', ', $return);
}
Эта функция может быть короче / лучше; но это ваша работа сейчас;)
Если вы хотите исключить праздничные дни, посмотрите этот пример: /questions/5265125/rasschitat-rabochie-dni/5265147#5265147
Я нашел это и преобразовал это в минуты,
$start = new DateTime('2014-03-03 09:21:30');
$end = new DateTime('2014-03-11 17:23:15');
// otherwise the end date is excluded (bug?)
$end->modify('+1 day');
$interval = $end->diff($start);
// total days
$days = $interval->days;
$days_inMin = ($interval->d*24*60) + ($interval->h*60) + $interval->i;
// 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('2014-03-07');
foreach($period as $dt) {
$curr = $dt->format('D');
// for the updated question
if (in_array($dt->format('Y-m-d'), $holidays)) {
$days--;
$days_inMin -= (24*60);
}
// substract if Saturday or Sunday
if ($curr == 'Sat' || $curr == 'Sun') {
$days--;
$days_inMin -= (24*60);
}
}
echo 'Days: ' . $days;
echo '<br>Days in Minutes: ' . $days_inMin . ' min = ' . $days_inMin/(24*60) . ' days';
Редактировать:
$office_hrs_min = $days_inMin - ($days * (14.5*60));
// as out of 24 only 8.5 hrs are working
Я получил ответ отсюда
Для PHP >= 5.3.0 используйте класс DatePeriod. К сожалению, это едва документировано.
$start = new DateTime('6/30/2010');
$end = new DateTime('7/6/2010');
$oneday = new DateInterval("P1D");
$days = array();
$data = "7.5";
/* Iterate from $start up to $end+1 day, one day in each iteration.
We add one day to the $end date, because the DatePeriod only iterates up to,
not including, the end date. */
foreach(new DatePeriod($start, $oneday, $end->add($oneday)) as $day) {
$day_num = $day->format("N"); /* 'N' number days 1 (mon) to 7 (sun) */
if($day_num < 6) { /* weekday */
$days[$day->format("Y-m-d")] = $data;
}
}
print_r($days);