时间段重合计算问题
在工作中,碰到了一个实际案例需要计算两个时间段的重合问题。具体问题是根据租赁时间和免租期来计算租金,而且租金计价方式不确定(可能按年,按月,按天计算)。
时间段重合计算
在本次实际案例中,实际上为去除掉两个时间段的重复部分,暂时没想到什么好的办法,就采用了罗列各种情况分别处理的方法:
public function date_count($sdate,$edate,$fee_sdate,$fee_edate){
// 判断合同是否设置免租期
if(!empty($fee_sdate) && !empty($fee_edate)){
// 设置了免租期,进行校验
if(!api_check_date_format($fee_sdate) || !api_check_date_format($fee_edate)){
// 此处校验时间戳格式是否正确
}elseif($fee_sdate > $fee_edate){
// 此处校验时间大小问题
}
if($fee_edate < $sdate || $fee_sdate > $edate){
// 免租期和租期不重合
$amount = $this->get_pay_amount($sdate,$edate);
}elseif($fee_sdate <= $sdate && $edate <= $fee_edate){
// 免租期包含租期 (fs---【s---e】---fe)
$amount = 0;
}elseif($sdate <= $fee_sdate && $fee_edate <= $edate){
// 租期包含免租期 【s---(fs---fe)---e】
if($sdate < $fee_sdate){
$pay_edate = date("Y-m-d",strtotime("-1day",strtotime($fee_sdate)));
$amount1 = $this->get_pay_amount($sdate,$pay_edate);
}else{
$amount1 = 0;
}
if($fee_edate < $edate){
$pay_sdate = date("Y-m-d",strtotime("+1day",strtotime($fee_edate)));
$amount2 = $this->get_pay_amount($pay_sdate,$edate);
}else{
$amount2 = 0;
}
// 此处两个四舍五入后的浮点数相加,可能导致精度不准确
$amount = $amount1 + $amount2;
}elseif($fee_sdate < $sdate && $sdate <= $fee_edate && $fee_edate < $edate){
// 免租期和租期前半部分重合 (fs---【s---fe)---e】
$pay_sdate = date("Y-m-d",strtotime("+1day",strtotime($fee_edate)));
$amount = $this->get_pay_amount($pay_sdate,$edate);
}elseif($sdate < $fee_sdate && $fee_sdate <= $edate && $edate < $fee_edate){
// 免租期和租期后半部分重合 【s---(fs---e】---fe)
$pay_edate = date("Y-m-d",strtotime("-1day",strtotime($fee_sdate)));
$amount = $this->get_pay_amount($sdate,$pay_edate);
}else{
// 此处担心会出现自己没有考虑到的额外情况,代码进行反馈
}
}else{
// 未设置免租期
$amount = $this->get_pay_amount($sdate,$edate);
}
return $amount;
}
时间段计算年月日数量
顺带写下根据时间段来计算该时间段有几年,几月,几天的数量:
public function count_date_detail($sdate,$edate){
$edate = date("Y-m-d",strtotime("+1day",strtotime($edate)));
$date_arr = [
'year_num'=>0,
'month_num'=>0,
'day_num'=>0
];
$year_num = api_date_numbers(strtotime($sdate),strtotime($edate),'y');
$new_edate = date("Y-m-d",strtotime("+".$year_num."years -1day",strtotime($sdate)));
if($new_edate == $edate){
$date_arr['year_num'] = $year_num;
return $date_arr;
}else{
if($new_edate > $edate){
$year_num -= 1;
}
$new_sdate = date("Y-m-d",strtotime("+".$year_num."years",strtotime($sdate)));
// 计算剩余月数
$month_num = api_date_numbers(strtotime($new_sdate),strtotime($edate),'m');
$new_edate = date("Y-m-d",strtotime("+".$month_num."months -1day",strtotime($new_sdate)));
if($new_edate == $edate){
$date_arr['year_num'] = $year_num;
$date_arr['month_num'] = $month_num;
return $date_arr;
}else{
if($new_edate > $edate){
$month_num -= 1;
}
$new_sdate = date("Y-m-d",strtotime("+".$month_num."months",strtotime($new_sdate)));
// 计算剩余天数
$day_num = api_date_numbers(strtotime($new_sdate),strtotime($edate),'d');
$new_edate = date("Y-m-d",strtotime("+".$day_num."days",strtotime($new_sdate)));
if($new_edate > $edate){
$day_num -= 1;
}elseif($new_edate < $edate){
$day_num += 1;
}
$date_arr['year_num'] = $year_num;
$date_arr['month_num'] = $month_num;
$date_arr['day_num'] = $day_num;
return $date_arr;
}
}
}
// 粗略计算有几年或者几月几天
function api_date_numbers($start,$end,$type='d'){
// 日期格式为2020-01-01,start和end均为时间戳
$start_m = date('Y-m-d',$start);
$end_m = date('Y-m-d',$end);
$date1 = explode('-',$start_m);
$date2 = explode('-',$end_m);
if ($type=='y'){
//取绝对值,避免因年份大小产生的负值
$number= abs($date1[0] - $date2[0]);
}elseif ($type=='m'){
//判断月份大小,进行相应加或减
if($date1[1]<$date2[1]){
$number= abs($date1[0] - $date2[0]) * 12 + abs($date1[1] - $date2[1]);
}else{
$number= abs($date1[0] - $date2[0]) * 12 - abs($date1[1] - $date2[1]);
}
}else{
$time = $end-$start;
$number = abs(intval($time/(3600*24)));
}
return $number;
}
如果有错误漏洞,或者更好的办法,欢迎大佬指正。