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.
Related
I have two arbitrary date ranges, for example:
2019-01-01 - 2019-01-10
and
2019-01-06 - 2019-01-20
How do I find out in PHP, by how many days these date ranges overlap? (In the example above it's 5 days)
Here's a solution using DateTime and DateInterval objects:
$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";
Output
overlap is 5 days
Demo on 3v4l.org
Update
Here is a more robust version of the code that allows for arbitrary overlapping of the ranges (including one being contained entirely in the other):
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";
}
It can be called like this:
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
Demo on 3v4l.org
I'm not quite sure, what you want to do, but check this solution:
<?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
More info about strtotime()
Notice: It will work only if second dates are always bigger that first ones. Your question is not clear.
Here's the function I use:
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;
}
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);
}
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");
}
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
I have a date like this
$start = strtotime('2010-01-01'); $end = strtotime('2010-01-25');
My question:
How can I calculate or count weekend from $start & $end date range..??
A more modern approach is using php's DateTime class. Below, you get an array with week numbers as keys. I added the counts of weeks and weekend days.
<?php
$begin = new DateTime('2010-01-01');
$end = new DateTime('2010-01-25');
$interval = new DateInterval('P1D');
$daterange = new DatePeriod($begin, $interval, $end);
$weekends = [];
foreach($daterange as $date) {
if (in_array($date->format('N'), [6,7])) {
$weekends[$date->format('W')][] = $date->format('Y-m-d');
}
}
print_r($weekends);
echo 'Number of weeks: ' . count($weekends);
echo 'Number of weekend days: ' . (count($weekends, COUNT_RECURSIVE) - count($weekends));
Note: if you're using PHP 5.3, use array() instead of block arrays [].
May be this code snippet will help:
<?php
//get current month for example
$beginday = date("Y-m-01");
$lastday = date("Y-m-t");
$nr_work_days = getWorkingDays($beginday, $lastday);
echo $nr_work_days;
function getWorkingDays($startDate, $endDate)
{
$begin = strtotime($startDate);
$end = strtotime($endDate);
if ($begin > $end) {
echo "startdate is in the future! <br />";
return 0;
} else {
$no_days = 0;
$weekends = 0;
while ($begin <= $end) {
$no_days++; // no of days in the given interval
$what_day = date("N", $begin);
if ($what_day > 5) { // 6 and 7 are weekend days
$weekends++;
};
$begin += 86400; // +1 day
};
$working_days = $no_days - $weekends;
return $working_days;
}
}
Another solution can be: (Get date range between two dates excluding weekends)
This might help maybe:
$start = strtotime('2010-01-01');
$end = strtotime('2010-01-25');
$differ = $end-$start;
$min = $differ/60;
$hrs = $min/60;
$days = $hrs/24;
$weeks = $days/7;
if(is_int($weeks))
$weeks++;
echo '<pre>';
print_r(ceil($weeks));
echo '</pre>';