Overlapping dates in Laravel - php

Previously I wanted to get time slots between $starttime and $endtime, divided by $duration value.
We managed to get it working with this code :
$starttime = '9:00'; // your start time
$endtime = '21:00'; // End time
$duration = '30'; // split by 30 mins
$array_of_time = array ();
$start_time = strtotime ($starttime); //change to strtotime
$end_time = strtotime ($endtime); //change to strtotime
$add_mins = $duration * 60;
while ($start_time <= $end_time) // loop between time
{
$array_of_time[] = date ("h:i", $start_time);
$start_time += $add_mins; // to check endtie=me
}
$new_array_of_time = array ();
for($i = 0; $i < count($array_of_time) - 1; $i++)
{
$new_array_of_time[] = '' . $array_of_time[$i] . ' - ' . $array_of_time[$i + 1];
}
Now the issue is I don't want to show the timeslots that match dates in my appointments table.
To get the Appointments matching that date , i did :
$appointments = Appointment::where('user_id', $user->id)->where('appointment_datetime', $date->toDateString() )->get();
Now this returns me all appointments, ... in each appointment we have a column appointment_start and appointment_end , witch are formated in TIME .
Next step would be to check all appointment for matching timeslots ... and this is where I am stuck. Thank you for your help.

Just replace the code with this one:
$starttime = '9:00'; // your start time
$endtime = '21:00'; // End time
$duration = '30'; // split by 30 mins
$array_of_time = array ();
$start_time = strtotime ($starttime); //change to strtotime
$end_time = strtotime ($endtime); //change to strtotime
$add_mins = $duration * 60; // seconds
while ($start_time <= $end_time) // loop between time
{
$array_of_time[] = date ("h:i", $start_time);
$start_time += $add_mins; // to check endtie=me
}
// Here I am getting the indexes of the time slot which has appointment
$indexes_to_be_skipped = array();
foreach($appointments as $appointment) {
for($i=0;$i<count($array_of_time); $i++) {
if($array_of_time[$i] == date ("h:i", strtotime($appointment['appointment_time_start']))) {
$indexes_to_be_skipped[$i] = $i;
}
}
}
$new_array_of_time = array ();
for($i = 0; $i < count($array_of_time) - 1; $i++)
{
$new_array_of_time[] = '' . $array_of_time[$i] . ' - ' . $array_of_time[$i + 1];
// check if current time slot has already appointment
if(isset($indexes_to_be_skipped[$i])) {
// then remove it
unset($new_array_of_time[$i]);
}
}
// resetting index
$narray_of_time = $new_array_of_time;
$new_array_of_time = array();
foreach($narray_of_time as $item) {
$new_array_of_time[] = $item;
}
I hope it helps!

This may help
<?php
$date = \Carbon\Carbon::now(); // the date you need to check
$starttime = \Carbon\Carbon::create($date->year, $date->month, $date->day, 9,0); // your start time
$endtime = \Carbon\Carbon::create($date->year, $date->month, $date->day, 21,0); // end time
$add_mins = 60; // minutes to add to each meeting
$array_of_time = [];
$current_time = $starttime->copy();
while($current_time->isBefore($endtime)){
// check if we have record between current and current + $add_mins
// I suppose appointment_datetime is a datetime field in database
if(!Appointment::where('user_id', $user->id)->whereBetween('appointment_datetime', [$current_time, $current_time->copy()->addMinute($add_mins)])->count()){
$array_of_time[] = $current_time->format('H:i') . ' - ' . $current_time->copy()->addMinute($add_mins)->format('H:i');
}
$current_time->addMinute($add_mins);
}

Related

I want to display time in a drop down using PHP. It will be be in chunks like 12:00, 12:15, 12:30 and so on

This is the code I am trying but it stops some time as now the value of i = 21 which is not <= 2. What should be the solution?
$pdts = array();
for($i = ltrim(date('H'), '0'); $i <= ltrim(date('H', time() + 14400), '0') * 2; $i++) {
for ($j = 15; $j <= 45; $j += 15) {
if ($j > ltrim(date('i'), '0') && ltrim(date('H'), '0') == $i) {
$date = date("H.i", strtotime("$i:$j"));
$value = $date."h";
$pdts[$value] = $date;
}
}
if (ltrim(date('i'), '0') != 0 && ltrim(date('H'), '0') != $i) {
$date = date("H.i", strtotime("$i:00"));
$value = $date."h";
$pdts[$value] = $date;
}
for ($k = 15; $k <= 45; $k += 15) {
if (ltrim(date('H'), '0') != $i) {
$date = date("H.i", strtotime("$i:$k"));
$value = $date . "h";
$pdts[$value] = $date;
}
}
}
As far as I understand you're trying to get 15-minutes chunks for the next 4 hours. There is built-in PHP DateTime / DateInterval / DatePeriod classes just for that. You can use them like that:
// current time - beginning of chunks
$begin = new DateTime();
// adjust $begin time for next '15/30/45/00' chunk
$next = $begin->format("i") % 15;
if ($next !== 0) {
$begin->modify('+' . (15 - $next) . 'minutes');
}
// time of last chunk
$end = clone $begin;
$end->modify("+4 hours");
// chunk interval (15 minutes)
$interval = new DateInterval('PT15M');
// date / time period onject
$timeRange = new DatePeriod($begin, $interval, $end);
$pdts = array();
foreach($timeRange as $time){
$pdts[] = $time->format("H:i");
}
Few words about code above:
1.Get current date & time. Current time is the beggining of time period to generate 15-minutes chuncks:
$begin = new DateTime();
2.Adjust current time to one of the 15-minutes chuncks. The easiest way to do it is to devide current amount of minutes by 15. If the reminder of devision is zero - than current time is OK and we can start from it. Otherwise we need to add (15 - reminder) minutes to current time to get valid start time:
$next = $begin->format("i") % 15;
if ($next !== 0) {
$begin->modify('+' . (15 - $next) . 'minutes');
}
3.To get end time of time period we need to add 4 hours to start time:
$end = clone $begin;
$end->modify("+4 hours");
4.We need to create time interval object with chunk duration:
$interval = new DateInterval('PT15M');
5.Create date period object (it will do all job for us)
$timeRange = new DatePeriod($begin, $interval, $end);
6.Iterate through date period object to gett all chunks
$pdts = array();
foreach($timeRange as $time){
$pdts[] = $time->format("H:i");
}

Extract only date from datetime for calculating total time in PHP

I have date time data in database and from that im calculating total hours only taking the date not the timing.
im using strtotime for that calculations. For ex:
$LastDate = '2016-04-27 17:27:28';
$endDate = strtotime($LastDate); // 1461770848
$LastDate = '2016-04-27';
$endDate = strtotime($LastDate); // 1461708000
I want this second value only.
So i want to extract the date from the $LastDate .
///////////Getting data from Employee Outstation(Travel) details from database/////////////
$employeeTravel = new EmployeeTravelRecord();
$TravelEntryList = $employeeTravel->Find("employee = 2 and (date(travel_date) <= ? and ? <= date(return_date))",array($req['date_end'],$req['date_start']));
$startdate = $req['date_start'];
$enddate = $req['date_end'];
foreach($TravelEntryList as $Travelentry){
$key = $Travelentry->employee;
if($startdate >= $Travelentry->travel_date)
{
$firstdate = $startdate;
}
else
$firstdate = $Travelentry->travel_date;
if($enddate <= $Travelentry->return_date )
{
$lastdate = $enddate;
}
else
$lastdate = $Travelentry->return_date;
$holidays = $this->getholidays($firstdate,$lastdate);
$totalhours = $this->getWorkingDays($firstdate,$lastdate,$holidays);
$amount = $totalhours;
}
private function getWorkingDays($FirstDate,$LastDate,$holidays){
$endDate = strtotime($LastDate); //Here i want to give only date not the time
$startDate = strtotime($FirstDate);
$days = ($endDate - $startDate) / 86400 + 1;
$no_full_weeks = floor($days / 7);
$no_remaining_days = fmod($days, 7);
//Im doing even and odd saturday calculations....finally...
$workinghours = $workingDays*9 + $no_saturdays*7;
return $workinghours;
}
such expression gives you desired result
echo strtotime('midnight', strtotime('2016-04-27 22:11')); // 1461715200
echo strtotime('2016-04-27'); // 1461715200

How to calculate working hours between two dates excluding holidays and weekends?

Let's say that I have two dates:
$initialDate = '08/10/2015 09:30:24 am';
$finalDate = '15/10/2015 15:47:38 pm';
$holiday = '12/10/2015';
I have to consider the hour of these days.
Hours to consider : 8 hours per day;
Start : 8 pm
End: 18 pm (24 hours format )
Lunch break start: 12:00 pm
Lunch break end: 14:00 pm
Example 1 : From 08/10/2015 10:00:00 to 09/10/2015 17:00:00 results 13 working hours. ( excludes lunch break )
Example 2 : From 08/10/2015 14:00:00 to 09/10/2015 18:00:00 results 12 working hours. ( Do not exclude 2 hours from begin date, because starts after 14:00 pm, lunch break )
Example 3 : From 08/10/2015 16:00:00 to 09/10/2015 18:00:00 results 10 working hours. ( Do not exclude 2 hours from begin date, because starts after 14:00 pmm lunch break )
Exampld 4 : From 08/10/2015 08:00:00 to 09/10/2015 11:00:00 results 14 working hours. ( Exclude 2 hours from begin date, and do not exclude 2 hours from end date, because isn't after 14:00 pm )
And I have to calculate the working hours and working days between those two dates, excluding weekends and Holidays, how can I do that ? I'm using PHP.
PS: I Already have something, but without lunch break... I made a research here on StackOverFlow.
Code:
function get_workdays($dataInicial,$dataFinal){
// arrays
$days_array = array();
$skipdays = array("Saturday", "Sunday");
$skipdates = get_feriados();
// other variables
$i = 0;
$current = $dataInicial;
if($current == $dataFinal) // same dates
{
$timestamp = strtotime($dataInicial);
if (!in_array(date("l", $timestamp), $skipdays)&&!in_array(date("Y-m-d", $timestamp), $skipdates)) {
$days_array[] = date("Y-m-d",$timestamp);
}
}
elseif($current < $dataFinal) // different dates
{
while ($current < $dataFinal) {
$timestamp = strtotime($dataInicial." +".$i." day");
if (!in_array(date("l", $timestamp), $skipdays)&&!in_array(date("Y-m-d", $timestamp), $skipdates)) {
$days_array[] = date("Y-m-d",$timestamp);
}
$current = date("Y-m-d",$timestamp);
$i++;
}
}
return $days_array;
}
function get_feriados(){
$dateAno = Date('Y');
$days_array = array(
$dateAno.'-10-12', // Padroeira do Brasil/ Dias das Crianças
$dateAno.'-11-02', // Finados
$dateAno.'-12-25' // Finados
);
return $days_array;
}
date_default_timezone_set('America/Sao_Paulo');
$dateAno = Date('Y');
$dataInicial = Date('08/10/2015 H:i');
$dataFinal = Date('13/10/2015 H:i');
// timestamps
$from_timestamp = strtotime(str_replace('/', '-', $dataInicial));
$to_timestamp = strtotime(str_replace('/', '-', $dataFinal));
// work day seconds
$workday_start_hour = 9;
$workday_end_hour = 17;
$workday_seconds = ($workday_end_hour - $workday_start_hour)*3600;
// work days beetwen dates, minus 1 day
$from_date = date('Y-m-d',$from_timestamp);
$to_date = date('Y-m-d',$to_timestamp);
$workdays_number = count(get_workdays($from_date,$to_date))-1;
$workdays_number = $workdays_number<0 ? 0 : $workdays_number;
// start and end time
$start_time_in_seconds = date("H",$from_timestamp)*3600+date("i",$from_timestamp)*60;
$end_time_in_seconds = date("H",$to_timestamp)*3600+date("i",$to_timestamp)*60;
// final calculations
$working_hours = ($workdays_number * $workday_seconds + $end_time_in_seconds - $start_time_in_seconds) / 86400 * 24;
print_r('<br/> Horas Ășteis '.$working_hours);
}
But don't consider two hours of break lunch. Can somebody please help me ?
If you use PHP 5.3 or higher, you can do this:
$datefrom = DateTime::createFromFormat('d/m/Y', '08/10/2015');
$dateto = DateTime::createFromFormat('d/m/Y', '15/10/2015');
$interval = $datefrom->diff($dateto);
$days = intval($interval->format('%a'));
Also you can remove holidays with if:
if ($datetime1->getTimestamp() < $holiday->getTimestamp() and $datetime2->getTimestamp() > $holiday->getTimestamp()) $days--;
Calculate hours between two days:
$datefrom = DateTime::createFromFormat('d/m/Y H:i:s', '08/10/2015 12:51:34');
$dateto = DateTime::createFromFormat('d/m/Y H:i:s', '15/10/2015 13:14:56');
$hours = intval($interval->format('%a')) * 24 + $interval->format('%h');
You can calculate hours of launches sum and then subtract it.
How to ignore weekends or calculate ignore days:
while($dateto->getTimestamp() > $datefrom->getTimestamp()) {
if (in_array($datefrom->format('w'), array('0','6'))) $ignore_days += 1;
$datefrom->modify('+1 day');
}
I expect this will do all you want. But I changed the datetime format as follows. Check it. Used less comments. If any query, please ask. Holidays are arrays, add and remove as required.
Times between 12:00 - 14:00 is handled.
Times below 08:00 is handled.
Times above 18:00 is handled.
<?php
$initialDate = '2015-10-13 08:15:00'; //start date and time in YMD format
$finalDate = '2015-10-14 11:00:00'; //end date and time in YMD format
$holiday = array('2015-10-12'); //holidays as array
$noofholiday = sizeof($holiday); //no of total holidays
//create all required date time objects
$firstdate = DateTime::createFromFormat('Y-m-d H:i:s',$initialDate);
$lastdate = DateTime::createFromFormat('Y-m-d H:i:s',$finalDate);
if($lastdate > $firstdate)
{
$first = $firstdate->format('Y-m-d');
$first = DateTime::createFromFormat('Y-m-d H:i:s',$first." 00:00:00" );
$last = $lastdate->format('Y-m-d');
$last = DateTime::createFromFormat('Y-m-d H:i:s',$last." 23:59:59" );
$workhours = 0; //working hours
for ($i = $first;$i<=$last;$i->modify('+1 day') )
{
$holiday = false;
for($k=0;$k<$noofholiday;$k++) //excluding holidays
{
if($i == $holiday[$k])
{
$holiday = true;
break;
} }
$day = $i->format('l');
if($day === 'Saturday' || $day === 'Sunday') //excluding saturday, sunday
$holiday = true;
if(!$holiday)
{
$ii = $i ->format('Y-m-d');
$f = $firstdate->format('Y-m-d');
$l = $lastdate->format('Y-m-d');
if($l ==$f )
$workhours +=sameday($firstdate,$lastdate);
else if( $ii===$f)
$workhours +=firstday($firstdate);
else if ($l ===$ii)
$workhours +=lastday($lastdate);
else
$workhours +=8;
}
}
echo $workhours; //echo the hours
}
else
echo "lastdate less than first date";
function sameday($firstdate,$lastdate)
{
$fmin = $firstdate->format('i');
$fhour = $firstdate->format('H');
$lmin = $lastdate->format('i');
$lhour = $lastdate->format('H');
if($fhour >=12 && $fhour <14)
$fhour = 14;
if($fhour <8)
$fhour =8;
if($fhour >=18)
$fhour =18;
if($lhour<8)
$lhour=8;
if($lhour>=12 && $lhour<14)
$lhour = 14;
if($lhour>=18)
$lhour = 18;
if($lmin == 0)
$min = ((60-$fmin)/60)-1;
else
$min = ($lmin-$fmin)/60;
return $lhour-$fhour + $min;
}
function firstday($firstdate) //calculation of hours of first day
{
$stmin = $firstdate->format('i');
$sthour = $firstdate->format('H');
if($sthour<8) //time before morning 8
$lochour = 8;
else if($sthour>18)
$lochour = 0;
else if($sthour >=12 && $sthour<14)
$lochour = 4;
else
{
$lochour = 18-$sthour;
if($sthour<=14)
$lochour-=2;
if($stmin == 0)
$locmin =0;
else
$locmin = 1-( (60-$stmin)/60); //in hours
$lochour -= $locmin;
}
return $lochour;
}
function lastday($lastdate) //calculation of hours of last day
{
$stmin = $lastdate->format('i');
$sthour = $lastdate->format('H');
if($sthour>=18) //time after 18
$lochour = 8;
else if($sthour<8) //time before morning 8
$lochour = 0;
else if($sthour >=12 && $sthour<14)
$lochour = 4;
else
{
$lochour = $sthour - 8;
$locmin = $stmin/60; //in hours
if($sthour>14)
$lochour-=2;
$lochour += $locmin;
}
return $lochour;
}
?>
Check the bellow code, that will return the number of Working days
function number_of_working_days($from, $to) {
$workingDays = [1, 2, 3, 4, 5];// date format = (1 = Monday,2 = Tue, ...)
$holidayDays = ['*-12-25', '*-02-14', '2015-12-23']; // variable and fixed holidays
$from = new DateTime($from);
$to = new DateTime($to);
$to->modify('+1 day');
$interval = new DateInterval('P1D');
$days = new DatePeriod($from, $interval, $to);
$no_of_working_days = 0;
foreach ($days as $day) {
if (!in_array($day->format('N'), $workingDays)||in_array($day->format('Y-m-d'), $holidayDays)||in_array($day->format('*-m-d'), $holidayDays)) {continue;}
$working_days++;
}
return $no_of_working_days;
}
echo number_of_working_days('2015-12-01', '2015-09-10');
From that you can easily calculate the Number of Working Hours.
I have created for you this nice class you can use. It requires the nesbot/carbon library (http://carbon.nesbot.com/) and you use it like so:
$calc = new HoursCalculator(
Carbon::createFromFormat("Y-m-d H:i", "2015-10-7 09:00"),
Carbon::createFromFormat("Y-m-d H:i", "2015-10-14 18:00"),
[
"2015-10-13"
]
);
echo $calc->getHours();
Heres the class:
class HoursCalculator {
const LUNCH_HOURS = 2;
protected $start;
protected $end;
protected $holidays;
protected $hoursTotal;
public function __construct(Carbon $start, Carbon $end, $holidays = [])
{
$this->start = $start;
$this->end = $end;
$this->holidays = $holidays;
}
public function getHours()
{
$dayHours = $this->getHoursInADay();
return $this->calculateHours($dayHours);
}
protected function getHoursInADay()
{
$start = $this->start;
$end = Carbon::createFromFormat("Y-m-d H:i", $this->start->format("Y-m-d") . " " . $this->end->format("H:i"));
return $start->diffInHours($end) - self::LUNCH_HOURS;
}
protected function getStartDate()
{
return $this->start->format('Y-m-d');
}
protected function calculateHours($hoursInDay)
{
$start = $this->start->copy()->startOfDay();
$end = $this->end->copy()->endOfDay();
$days = 0;
while($start->lt($end)) {
if (!$this->isHoliday($start) && !$this->isWeekend($start)) {
$days++;
}
$start->addDay(1);
}
return $days * $hoursInDay;
}
protected function isHoliday(Carbon $date)
{
$date->startOfDay();
foreach($this->holidays as $holiday) {
$holiday = Carbon::createFromFormat("Y-m-d", $holiday)->startOfDay();
if ($date->eq($holiday)) {
return true;
}
}
return false;
}
protected function isWeekend(Carbon $date)
{
return $date->isWeekend();
}
}
Hope this helps!

Average day of month from list of dates in PHP

I'm trying to find the average date and guess the next one.
The input is a list of dates that looks like this:
$completeDate = array(
'2015-04-13T00:00:00-0800',
'2015-03-20T00:00:00-0800',
'2015-02-17T00:00:00-0800',
'2015-01-10T00:00:00-0800'
);
I'm trying to scan a list of x amount of dates, and output an average of the dates overall.
So in the above example I think the output would be 2015-5-15 is expected average date.
How would I tackle this?
If you're looking for the average of those dates you can simply get the day of the year for each of those dates, average them out, and use that date:
$completeDate = array(
'2015-04-13T00:00:00-0800',
'2015-03-20T00:00:00-0800',
'2015-02-17T00:00:00-0800',
'2015-01-10T00:00:00-0800'
);
$first = null;
$last = null;
foreach($completeDate as $date) {
$dayOfYear = (new DateTime($date))->format('z');
if (is_null($first)) {
$first = $last = $dayOfYear;
}
else {
if ($dt < $first) {
$first = $dayOfYear;
}
if ($dt > $last) {
$last = $dayOfYear;
}
}
}
$avg = round(($first + $last) / 2);
$averageDate = DateTime::createFromFormat('z', $avg);
echo $averageDate->format('Y-m-d'); // 2015-02-26
Demo
If your looking for the average of the day of the month for the dates in that array and then use that day of the next month, you just need to average out the days of the month and then use that with the next month:
$completeDate = array(
'2015-04-13T00:00:00-0800',
'2015-03-20T00:00:00-0800',
'2015-02-17T00:00:00-0800',
'2015-01-10T00:00:00-0800'
);
$month = 0;
$days = 0;
foreach($completeDate as $date) {
$dt = new DateTime($date);
$month_num = $dt->format('n');
if ($month_num > $month) {
$month = $month_num;
}
$days += $dt->format('j');
}
$avg = round($days / count($completeDate));
$date = new DateTime(sprintf('%d-%01d-%01d', $dt->format('Y'), ++$month, $avg));
echo $date->format('Y-m-d'); // 2015-05-15
Demo

Php DateTime and timestamp calculation

I'm trying to get an array of dates to populate a calendar and I'm facing an the issue of infinite loop.
Here is the code:
$month_length = 1;
$day_begin = 9;
$day_end = 19;
$event_interval = 15;
$date = new DateTime();
$today_date_name = $date->format("D");
$today_date_number = $date->format("j");
$today_date_month = $date->format("M");
$today_date_month_number =$date->format("n");
$today_date_year = $date->format("Y");
for ($length = 0; $length <= ($month_length - 1); $length++)
{
$date->modify("+$length month");
$current_date_name = $date->format("D");
$current_date_number = $date->format("j");
$current_date_month = $date->format("M");
$current_date_month_number = $date->format("n");
$current_date_year = $date->format("Y");
//calculate the length of the month
$current_month_length = cal_days_in_month(CAL_GREGORIAN, $current_date_month_number, $current_date_year);
if($current_date_month_number != $today_date_month_number){
// if we are not in the current month, start the second loop
// the first of the month.
$current_date_number = 1;
}
for($current_date_number; $current_date_number <= $current_month_length; $current_date_number++)
{
$date->setDate($current_date_year, $current_date_month_number, $current_date_number);
//set the ending before because of the loop;
//set the ending of the day
$date->setTime($day_end, 0, 0);
//get the timestamp of beginning
$ending_timestamp = $date->format("U");
//set the beginning of the day
$date->setTime($day_begin, 0, 0);
//get the timestamp of beginning
$beginning_timestamp = $date->format("U");
$day_length = $ending_timestamp - $beginning_timestamp;
//60 seconds for 1min
$interval = 60 * $event_interval;
for($the_timestamp = 0; $the_timestamp <= $day_length ; $the_timestamp + $interval)
{
$current_timestamp = $beginning_timestamp + $the_timestamp;
$date->setTimestamp($current_timestamp);
$final_array[$current_date_year][$current_date_number . " " . $current_date_month][$current_timestamp] = $date->format("H : i");
}
}
}
the last "for" loop ends with an infinite loop and so reaches the maximum execution time, it must be done under 30s.
Your code is far too complex, you are doing something wrong. The following will generate an array representing a whole year:-
$start = new \DateTime('1st January');
$end = new \DateTime('31st December');
$interval = new \DateInterval('P1D');
$period = new \DatePeriod($start, $interval, $end->modify('+ 1 day'));
$year = array();
foreach($period as $day){
$year[$day->format('M')][(int)$day->format('d')] = $day->format('D');
}
var_dump($year);
See it working
for($the_timestamp = 0; $the_timestamp <= $day_length ; $the_timestamp + $interval){
//Stuff
}
This loop doesn't increment $the_timestamp which is why it loops forever.
The last part should be $the_timestamp += $interval which is equivalent to $the_timestamp = $the_timestamp + $interval.

Categories