找出两个日期范围在 PHP 中重叠的天数
Find out by how many days two date ranges overlap in PHP
我有两个任意日期范围,例如:
2019-01-01 - 2019-01-10
and
2019-01-06 - 2019-01-20
如何在 PHP 中找出这些日期范围重叠的天数? (在上面的例子中是 5 天)
我不太确定你想做什么,但请检查这个解决方案:
<?php
$date1 = strtotime('2019-01-01');
$date2 = strtotime('2019-01-10');
$date3 = strtotime('2019-01-06');
$date4 = strtotime('2019-01-20');
$dateDiff1 = $date2 - $date1;
$dateDiff2 = $date4 - $date3;
$finalDiff = $dateDiff2 - $dateDiff1;
echo round($finalDiff / (60 * 60 * 24));
Return:
5
有关 strtotime()
的更多信息
注意:只有当第二个日期总是比第一个日期大时,它才会起作用。你的问题不清楚。
这是一个使用 DateTime
and DateInterval
对象的解决方案:
$range1 = '2019-01-01 - 2019-01-10';
$range2 = '2019-01-06 - 2019-01-20';
list($start, $end) = explode(' - ', $range1);
$start1 = new DateTime($start);
$end1 = new DateTime($end);
list($start, $end) = explode(' - ', $range2);
$start2 = new DateTime($start);
$end2 = new DateTime($end);
if ($end1 > $start1) {
$overlap = $end1->diff(min($start2, $end2));
}
else {
$overlap = $start1->diff(min($start2, $end2));
}
echo "overlap is " . ($overlap->format('%a') + 1) . " days";
输出
overlap is 5 days
更新
这是一个更强大的代码版本,允许范围任意重叠(包括一个完全包含在另一个范围内):
function range_overlap($range1, $range2) {
list($start, $end) = explode(' - ', $range1);
$start = new DateTime($start);
$end = new DateTime($end);
$start1 = min($start, $end);
$end1 = max($start, $end);
list($start, $end) = explode(' - ', $range2);
$start = new DateTime($start);
$end = new DateTime($end);
$start2 = min($start, $end);
$end2 = max($start, $end);
// check for special cases
if ($start1 >= $start2 && $end1 <= $end2) {
// range1 completely contained inside range2
$overlap = $start1->diff($end1);
}
elseif ($start2 >= $start1 && $end2 <= $end1) {
// range2 completely contained inside range1
$overlap = $start2->diff($end2);
}
elseif ($end2 > $end1) {
// range1 ends first
$overlap = $start2->diff($end1);
}
else {
// range2 ends first
$overlap = $start1->diff($end2);
}
// if overlap is < 0 then there is no overlap
$overlap_days = $overlap->invert ? 0 : ($overlap->format('%a') + 1);
echo "overlap is $overlap_days days\n";
}
可以这样调用:
range_overlap('2019-01-01 - 2019-01-10', '2019-01-06 - 2019-01-20'); // 5 days
range_overlap('2019-01-01 - 2019-03-20', '2019-05-06 - 2019-04-20'); // no overlap
range_overlap('2019-01-10 - 2019-05-20', '2019-01-01 - 2019-05-20'); // 131 days
range_overlap('2019-01-06 - 2019-01-20', '2019-01-10 - 2019-01-01'); // 5 days
range_overlap('2019-01-30 - 2019-01-10', '2019-01-12 - 2019-01-15'); // 4 days
range_overlap('2019-02-01 - 2019-03-20', '2019-01-10 - 2019-02-28'); // 28 days
这是我使用的函数:
function nightsInRange(\Datetime $arriveDate, \Datetime $departDate, \Datetime $rangeStart, \Datetime $rangeEnd) : int
{
// just use the Y-m-d portion of date
$arriveDate = clone $arriveDate;
$arriveDate->setTime(0,0,0);
$departDate = clone $departDate;
$departDate->setTime(0,0,0);
$rangeStart = clone $rangeStart;
$rangeStart->setTime(0,0,0);
$rangeEnd = clone $rangeEnd;
$rangeEnd->setTime(0,0,0);
if ($arriveDate >= $departDate) {
throw new \InvalidArgumentException("arriveDate must be BEFORE departDate");
}
if ($rangeStart > $rangeEnd) {
throw new \InvalidArgumentException("rangeEnd must be greater than or equal to rangeStart");
}
$arriveDateInRange = ($arriveDate >= $rangeStart && $arriveDate <= $rangeEnd);
$departDateInRange = ($departDate >= $rangeStart && $departDate <= $rangeEnd);
if ($arriveDateInRange && $departDateInRange) {
// both dates inside range
$nightsInRange = $arriveDate->diff($departDate)->days;
} elseif ($arriveDateInRange && !$departDateInRange) {
// arrive inside, depart outside
$nightsInRange = $arriveDate->diff($rangeEnd)->days + 1;
} elseif (!$arriveDateInRange && $departDateInRange) {
// arrive outside, depart inside
$nightsInRange = $departDate->diff($rangeStart)->days;
} elseif ($arriveDate <= $rangeStart && $departDate >= $rangeEnd) {
// arrive before rangeStart, depart after rangeEnd
//
// Note that we add 1 to the date diff to get the range length. For
// example, the range from 2018-11-01 to 2018-11-30 is 30 days (not 29).
// A range with same start and end dates (e.g. 2018-11-01 to 2018-11-01)
// would have a range length of 1 day.
//
$nightsInRange = $rangeStart->diff($rangeEnd)->days + 1;
} else {
// no overlap
$nightsInRange = 0;
}
return $nightsInRange;
}
我有两个任意日期范围,例如:
2019-01-01 - 2019-01-10
and
2019-01-06 - 2019-01-20
如何在 PHP 中找出这些日期范围重叠的天数? (在上面的例子中是 5 天)
我不太确定你想做什么,但请检查这个解决方案:
<?php
$date1 = strtotime('2019-01-01');
$date2 = strtotime('2019-01-10');
$date3 = strtotime('2019-01-06');
$date4 = strtotime('2019-01-20');
$dateDiff1 = $date2 - $date1;
$dateDiff2 = $date4 - $date3;
$finalDiff = $dateDiff2 - $dateDiff1;
echo round($finalDiff / (60 * 60 * 24));
Return:
5
有关 strtotime()
的更多信息注意:只有当第二个日期总是比第一个日期大时,它才会起作用。你的问题不清楚。
这是一个使用 DateTime
and DateInterval
对象的解决方案:
$range1 = '2019-01-01 - 2019-01-10';
$range2 = '2019-01-06 - 2019-01-20';
list($start, $end) = explode(' - ', $range1);
$start1 = new DateTime($start);
$end1 = new DateTime($end);
list($start, $end) = explode(' - ', $range2);
$start2 = new DateTime($start);
$end2 = new DateTime($end);
if ($end1 > $start1) {
$overlap = $end1->diff(min($start2, $end2));
}
else {
$overlap = $start1->diff(min($start2, $end2));
}
echo "overlap is " . ($overlap->format('%a') + 1) . " days";
输出
overlap is 5 days
更新
这是一个更强大的代码版本,允许范围任意重叠(包括一个完全包含在另一个范围内):
function range_overlap($range1, $range2) {
list($start, $end) = explode(' - ', $range1);
$start = new DateTime($start);
$end = new DateTime($end);
$start1 = min($start, $end);
$end1 = max($start, $end);
list($start, $end) = explode(' - ', $range2);
$start = new DateTime($start);
$end = new DateTime($end);
$start2 = min($start, $end);
$end2 = max($start, $end);
// check for special cases
if ($start1 >= $start2 && $end1 <= $end2) {
// range1 completely contained inside range2
$overlap = $start1->diff($end1);
}
elseif ($start2 >= $start1 && $end2 <= $end1) {
// range2 completely contained inside range1
$overlap = $start2->diff($end2);
}
elseif ($end2 > $end1) {
// range1 ends first
$overlap = $start2->diff($end1);
}
else {
// range2 ends first
$overlap = $start1->diff($end2);
}
// if overlap is < 0 then there is no overlap
$overlap_days = $overlap->invert ? 0 : ($overlap->format('%a') + 1);
echo "overlap is $overlap_days days\n";
}
可以这样调用:
range_overlap('2019-01-01 - 2019-01-10', '2019-01-06 - 2019-01-20'); // 5 days
range_overlap('2019-01-01 - 2019-03-20', '2019-05-06 - 2019-04-20'); // no overlap
range_overlap('2019-01-10 - 2019-05-20', '2019-01-01 - 2019-05-20'); // 131 days
range_overlap('2019-01-06 - 2019-01-20', '2019-01-10 - 2019-01-01'); // 5 days
range_overlap('2019-01-30 - 2019-01-10', '2019-01-12 - 2019-01-15'); // 4 days
range_overlap('2019-02-01 - 2019-03-20', '2019-01-10 - 2019-02-28'); // 28 days
这是我使用的函数:
function nightsInRange(\Datetime $arriveDate, \Datetime $departDate, \Datetime $rangeStart, \Datetime $rangeEnd) : int
{
// just use the Y-m-d portion of date
$arriveDate = clone $arriveDate;
$arriveDate->setTime(0,0,0);
$departDate = clone $departDate;
$departDate->setTime(0,0,0);
$rangeStart = clone $rangeStart;
$rangeStart->setTime(0,0,0);
$rangeEnd = clone $rangeEnd;
$rangeEnd->setTime(0,0,0);
if ($arriveDate >= $departDate) {
throw new \InvalidArgumentException("arriveDate must be BEFORE departDate");
}
if ($rangeStart > $rangeEnd) {
throw new \InvalidArgumentException("rangeEnd must be greater than or equal to rangeStart");
}
$arriveDateInRange = ($arriveDate >= $rangeStart && $arriveDate <= $rangeEnd);
$departDateInRange = ($departDate >= $rangeStart && $departDate <= $rangeEnd);
if ($arriveDateInRange && $departDateInRange) {
// both dates inside range
$nightsInRange = $arriveDate->diff($departDate)->days;
} elseif ($arriveDateInRange && !$departDateInRange) {
// arrive inside, depart outside
$nightsInRange = $arriveDate->diff($rangeEnd)->days + 1;
} elseif (!$arriveDateInRange && $departDateInRange) {
// arrive outside, depart inside
$nightsInRange = $departDate->diff($rangeStart)->days;
} elseif ($arriveDate <= $rangeStart && $departDate >= $rangeEnd) {
// arrive before rangeStart, depart after rangeEnd
//
// Note that we add 1 to the date diff to get the range length. For
// example, the range from 2018-11-01 to 2018-11-30 is 30 days (not 29).
// A range with same start and end dates (e.g. 2018-11-01 to 2018-11-01)
// would have a range length of 1 day.
//
$nightsInRange = $rangeStart->diff($rangeEnd)->days + 1;
} else {
// no overlap
$nightsInRange = 0;
}
return $nightsInRange;
}