Reputation: 1333
I am using Laravel-5.8 to develop a Staff Leave platform.
Currently, I am on Leave Request. At the beginning of every year the Admin set all the holidays and store in hr_holiday_dates table as shown below:
class HrHolidayDate extends Model
{
protected $table = 'hr_holiday_dates';
protected $primaryKey = 'id';
protected $fillable = [
'holiday_name',
'date_from',
'date_to',
];
}
The employee logs in into the application to make leave request.
Model: HrLeaveRequest:
class HrLeaveRequest extends Model
{
public $timestamps = false;
protected $table = 'hr_leave_requests';
protected $primaryKey = 'id';
protected $fillable = [
'id',
'employee_id',
'reason',
'leave_type_id',
'commencement_date',
'resumption_date',
'no_of_days',
];
public function employee()
{
return $this->belongsTo('App\Models\Hr\HrEmployee','employee_id');
}
public function leaveType()
{
return $this->belongsTo('App\Models\Hr\HrLeaveType','leave_type_id');
}
}
When the employee makes request for leave, he selects leave_type, commencement_date and resumption_date. The no_of_days is not selected, but is based on the application intelligence.
Controller
public function create()
{
return view('hr.leave_requests.create');
}
public function store(StoreLeaveRequest $request)
{
try {
$commencementDate = Carbon::parse($request->commencement_date);
$resumptionDate = Carbon::parse($request->resumption_date);
$employeeId = Auth::user()->employee_id;
$leave = new HrLeaveRequest();
$leave->leave_type_id = $request->leave_type_id;
$leave->commencement_date = $commencementDate ->toDateTimeString();
$leave->resumption_date = resumptionDate ->toDateTimeString();
$leave->no_of_days = $request->no_of_days;
Session::flash('success', 'Leave Request is created successfully');
return redirect()->route('hr.leave_requests.index');
} catch (Exception $exception) {
Session::flash('error', 'Action failed! Please try again');
return redirect()->route('hr.leave_requests.index');
}
}
For leave no_of_days, what I want is this, the application get it between commencement_date and resumption_date. But before it does that, it removes weekend (Saturday and Sunday) from the selected dates. Also, it goes to hr_holiday_days that falls into the range of the selected dates and extract the holidays. It should also put into consideration the weekend in the holiday.
How do I get:
$leave->no_of_days
from the explanation above?
Thank you.
Upvotes: 2
Views: 1564
Reputation: 602
The code below should be able to help you;
$start = Carbon::create(2022, 12, 25);
$end = Carbon::create(2022, 12, 31);
$holidays = [
Carbon::create(2022, 12, 25),
Carbon::create(2022, 12, 26)
];
$days = $start->diffInDaysFiltered(function (Carbon $date) use ($holidays) {
return $date->isWeekday() && !in_array($date, $holidays);
}, $end, false);
dd($days);
Upvotes: 0
Reputation: 126
Enter the starting and ending dates, along with an array of any holidays that might be in between, and it returns the working days as an integer:
<?php
//The function returns the no. of business days between two dates and it skips the holidays
function getWorkingDays($startDate,$endDate,$holidays){
// 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;
}
//We subtract the holidays
foreach($holidays as $holiday){
$time_stamp=strtotime($holiday);
//If the holiday doesn't fall in weekend
if ($startDate <= $time_stamp && $time_stamp <= $endDate && date("N",$time_stamp) != 6 && date("N",$time_stamp) != 7)
$workingDays--;
}
return $workingDays;
}
//Example:
$holidays=array("2008-12-25","2008-12-26","2009-01-01");
echo getWorkingDays("2008-12-22","2009-01-02",$holidays)
// => will return 7
?>
Referenced from : https://stackoverflow.com/a/336175/10309265
Upvotes: 1