i have multiple jquery fullcalendars on a page which is used for machine scheduling in our software.
each calender will make a ajax call once a specific input for the workload changes.
In my AJAX script i need to calculate the dates that will be disabled in the calendar based upon the already planned time of the machine and whether or not there are workers with enough time avail on that day.
so here is what i got so far:
$retval = Array();
$objectid = $_REQUEST["objectid"];
$type = $_REQUEST["type"];
$amount = $_REQUEST["amount"];
$date = $_REQUEST["date"];
$month = date("m",strtotime($date));
$year = date("Y",strtotime($date));
if ($month == date("m") && $year == date("Y"))
{
$start = mktime(0,0,0,date("m"),date("d"),date("Y"));
} else {
$mktime1 = mktime(0,0,0,$month,24,$year);
$start = strtotime('-1 month',$mktime1);
}
$mktime2 = mktime(0,0,0,$month,6,$year);
$end = strtotime('+2 month',$mktime2);
$job_arr = Array();
$jobs = PlanningJob::getAllJobs(" AND start>".$start." AND end<".$end);
foreach ($jobs as $job)
{
$date = mktime(0,0,0,date("m",$job->getDate()),date("d",$job->getDate()),date("Y",$job->getDate()));
$job_arr[$date] = 0;
if ($job->getTime()>0)
$job_arr[$date] += $job->getTime();
elseif ($job->getPlannedTime()>0)
$job_arr[$date] += $job->getPlannedTime();
}
if ($type == "ME"){
$me = new Machineentry($objectid);
$machine = $me->getMachine();
for ( $i = $start; $i <= $end; $i = $i + 86400 ) {
$date = mktime(0,0,0,date("m",$i),date("d",$i),date("Y",$i));
$total_seconds = $machine->getRunningtimeForDay($date);
if (isset($job_arr[$date]))
{
$total_seconds -= ($job_arr[$date]*60*60);
}
if ($total_seconds < ($amount*60*60))
$retval[] = date("d.m.Y",$date);
}
} else if ($type == "OP")
{
$op = new Orderposition($objectid);
$jobart = new Article($op->getObjectid());
}
now i wonder if there is a better approach to calculating for so many dates (date range) at once - performance wise. as this one is quite heavy and takes a couple of seconds which really slows down our scheduling process.
i would appreciate any help/advice!
Kind Regards,
Alex
[EDIT] some more info's on what i need to output:
One Array containing: all dates within the range
and within this array per date:
1. all users having spare time on that day and the specific amount of time
2. total time scheduled on the machine that day
Second Array
Per date: if not enough 'free' time on the machine only the date so it can be disabled in the calendar
Here is a function I wrote a while back which will take two dates and return an array of the range between them. I use this function on a variety of things and it's very fast.
Dates provided for $dateFrom and $dateTo should be in YYYY-MM-DD format.
function createDateRangeArray($dateFrom,$dateTo)
{
$arrayRange=array();
$dateFrom=mktime(1,0,0,substr($dateFrom,5,2), substr($dateFrom,8,2),substr($dateFrom,0,4));
$dateTo=mktime(1,0,0,substr($dateTo,5,2), substr($dateTo,8,2),substr($dateTo,0,4));
if ($dateTo>=$dateFrom)
{
array_push($arrayRange,date('Y-m-d',$dateFrom));
while ($dateFrom<$dateTo)
{
$dateFrom+=86400;
array_push($arrayRange,date('Y-m-d',$dateFrom));
}
}
return $arrayRange;
}
Related
I really need help to display a month list view in which deposit received month should be green in color and no-deposit month should be in red color.
I have stuck in comparing the array values. and I am using codeigniter.
I have a table with start-date and end-date by using this I have created an array to display all months in between these dates. Please find the code which I have used to do it below:
$datefrom = strtotime($showrangecalendar['chitdate_start']);
$dateto = strtotime($showrangecalendar['chitdate_end']);
$start_date = date('Y-m-d', $datefrom);
$end_date = date('Y-m-d', $dateto);
$day = 2.628e+6; // Day in Months
$format = 'Y-F'; // Output format (see PHP date funciton)
$sTime = strtotime($start_date); // Start as time
$eTime = strtotime($end_date); // End as time
$numDays = round(($eTime - $sTime) / $day) + 1;
$days = array();
for ($d = 0; $d < $numDays; $d++) {
$days[] = date($format, ($sTime + ($d * $day)));
}
This code got me the Days list and I have displayed this using foreach statement to a table.
Now I have another table (accounts) with columns such as ID, Book_number, Deposit_Amount and Deposit_Month. Here the problem starts I have fetched data from "accounts" and loaded to my view using the below code from controller
$data['show_account']= $this->Accounts_model->get_all_accounts($sess_data);
My Model (Accounts_Model) is as shown below:
public function get_all_accounts($id)
{
$this->db->select("*");
$this->db->order_by('book_id','desc');
$this->db->from("accounts");
$this->db->where('bh_id',$id);
$query = $this->db->get();
return $query->result();
}
If I run the belowcode:
foreach($show_account as $values){
echo $values->deposit_month;
}
Its getting me the array result of all deposit month. Suppose I have data as 2018-Sep and 2018-Oct, These 2 months column should turn green in the above mentioned $days array.
Hope I have explained my requirement clearly. Please help me with this as I am already spent long hours in this.
Thanks in Advance.
Updated:
Now could you please check my model as follows:
public function get_dep_month($bh_id)
{
$this->db->select('deposit_month');
$this->db->from('accounts');
$this->db->where('bh_id',$_SESSION['bh_id']);
$this->db->order_by('deposit_month','asc');
$query = $this->db->get();
return $query->result_array();
}
And My Controller is as follows:
$sess_data = $this->session->userdata('bh_id');
$data['depM_array'] = $this->Accounts_model->get_dep_month($sess_data);
Now please check my View as follows:
<?php
// option 2
foreach($days as $day) { // using your already exist $day for-loop for display
if (!in_array($day, $depM_array)){
$calhilight = "calgreen";
}
else{
$calhilight = "calred";
}
?>
<li class="<?php echo $calhilight; ?>"><?= $day ?></li>
<?php
}
?>
But as my doposit_month column having only 2 values ie: 2018-Sep and 2018-Oct, Instead of getting green color for those 2 values, I am getting Green for who li element. Not getting where I have done wrong.
This is the Current page view which I am getting, Actually I am expecting a calendar view with 2 green fields for 2018-Sep and 2018-Oct and all other fields in Red
Performed Var Dumb in arrays:
Pls check this screenshot
FYI:
Below is the code from where I am getting $days array.
<?php
$datefrom = strtotime($showrangecalendar['chitdate_start']);
$dateto = strtotime($showrangecalendar['chitdate_end']);
$start_date = date('Y-m-d', $datefrom);
$end_date = date('Y-m-d', $dateto);
$day = 2.628e+6; // Day in Months
$format = 'Y-F'; // Output format (see PHP date funciton)
$sTime = strtotime($start_date); // Start as time
$eTime = strtotime($end_date); // End as time
$numDays = round(($eTime - $sTime) / $day) + 1;
$days = array();
for ($d = 0; $d < $numDays; $d++) {
$days[] = date($format, ($sTime + ($d * $day)));
}
?>
Here $start_date & $end_date is fetching from another table named chitdate.
Thanks Again.
You can use array-diff or simple for-loop.
Consider the following simple example:
$days = array("2018-September", "2018-October", "2018-November", "2018-December");
$deposit_month = array("2018-September", "2018-October");
// option 1:
$diff = array_diff($days, $deposit_month);
// now $diff has: "2018-November" and "2018-December"
// option 2
foreach($days as $day) { // using your already exist $day for-loop for display
if (!in_array($day, $deposit_month))
// color in red as not found in "deposit_month"
else
// color in green
}
Nicer writing for that if can be as:
$color = in_array($day, $deposit_month) ? "Green" : "Red";
I would like to get the first date in the future from an array of dates. I've tried to write my own function, which did not get the job done.
private static function getClosestDate($date, array $dates, $last) {
$interval = array();
$now = strtotime(date('Y-m-d'));
foreach ($dates as $d) {
$dateTime = strtotime($date);
$toTime = strtotime($d);
// Do not parse dates older than today
if (strtotime($d) < $now) { continue 1; }
// Only do dates in the future
if ($toTime < $dateTime) { continue 1; }
$interval[] = abs($dateTime - $toTime);
}
// If there is no interval, use the latest date
if (!count($interval)) {
return $last;
}
asort($interval);
$closest = key($interval);
return $dates[$closest];
}
This code works but it also works the other way around. Next to that, when there is no last date, it should use the $last parameter which is the last date.
The reason I want to know this is because I have a range of dates on which people can book nights on. I want to know the difference in days to calculate the amount of nights they can book. Calculating a difference in dates is easy, but I do need to know when the next booking appears. These are provided in the $dates parameter.
What should I change in my code to get it fixed (I've tried to continue the foreach whenever the date was in the past based on the $date parameter) or can someone provide me the code?
I've fixed the problem myself. Instead of using the dates array I'm now using the date as key in the interval to retrieve the date again. This does work properly.
private static function getClosestDate($date, array $dates, $last) {
$interval = array();
$now = strtotime(date('Y-m-d'));
foreach ($dates as $d) {
$dateTime = strtotime($date);
$toTime = strtotime($d);
// Do not parse dates older than today
if (strtotime($d) < $now) { continue 1; }
// Only do dates in the future
if ($toTime < $dateTime) { continue 1; }
$interval[$d] = abs($dateTime - $toTime);
}
// If there is no interval, use the latest date
if (!count($interval)) {
return $last;
}
asort($interval);
$closest = key($interval);
return $closest;
}
I'm using the following code to calculate the average time between a start time and an end time for a number of events (from a database):
function getATBData($siteID, $fromDate, $toDate)
{
global $pdo;
$ATBarray = array();
$maxATB;
$minATB;
$avgATB;
$totalATB=new DateTime("#0");
$totalEvents=0;
$timetable;
$query = "SELECT id, siteID, start_time, end_time FROM atb_log WHERE siteID=:siteID AND (start_time BETWEEN :fromDate AND :toDate) AND (end_time BETWEEN :fromDate AND :toDate)";
$stmt = $pdo->prepare($query);
$stmt->bindParam(":siteID", $siteID);
$stmt->bindParam(":fromDate", $fromDate);
$stmt->bindParam(":toDate", $toDate);
$stmt->execute();
foreach ($stmt as $row)
{
$timeDiff = date_diff(new DateTime($row['start_time']),new DateTime($row['end_time']), true); //force absolute
if(!isset($maxATB) OR dateIntervalInSeconds($timeDiff) > dateIntervalInSeconds($maxATB))
$maxATB = $timeDiff;
if(!isset($minATB) OR dateIntervalInSeconds($timeDiff) < dateIntervalInSeconds($minATB))
$minATB = $timeDiff;
$totalATB->add($timeDiff);
$totalEvents++;
}
if($totalEvents!=0)
{
//$avgATB=round($totalATB->getTimestamp() / $totalEvents);
$avgATB = average_time($totalATB->format("H:i:s"),$totalEvents,0);
}
else
{
$avgATB=0;
$maxATB=new DateInterval('PT0S');
$minATB=new DateInterval('PT0S');
}
$avgSeconds = new DateInterval("PT" . $avgATB . "S");
$ATBarray['max'] = $maxATB->format("%H:%I:%S");
$ATBarray['min'] = $minATB->format("%H:%I:%S");
$ATBarray['avg'] = gmdate("H:i:s",$avgATB); //$avgSeconds->format("%H:%i:%s");
$ATBarray['total'] = $totalATB->format("H:i:s");
$ATBarray['events'] = $totalEvents;
return $ATBarray;
}
Unfortunately, I'm getting extremely high averages. As an example, I'm finding that the max time is 3 seconds, the min time is 0 seconds, but the average time is 1 hour and 1 second. This is obviously impossible, so is there something wrong with how I'm calculating total and/or average? The total is rather high too, but I haven't been able to manually add up this data so I'm not sure if that is also incorrect.
This is most probably a DST problem. Is your code running in a U.S. timezone? Rerember that when you're using new DateTime("#0"), this represents 1970-01-01 00:00:00 (non-DST); in the U.S., DST has started now, so the absolute value of your DateTime objects created from the database are one hour off.
EDIT:
I've changed the way I'm calculating averages. I've added the new function all above, and here is the averaging function (found here):
function average_time($total, $count, $rounding = 0) //from SO
{
$total = explode(":", strval($total));
if (count($total) !== 3) return false;
$sum = $total[0]*60*60 + $total[1]*60 + $total[2];
$average = $sum/(float)$count;
$hours = floor($average/3600);
$minutes = floor(fmod($average,3600)/60);
$seconds = number_format(fmod(fmod($average,3600),60),(int)$rounding);
if($hours<10) $hours = "0"+$hours; //add leading 0
if($minutes<10) $minutes = "0"+$minutes;
if($seconds<10) $seconds = "0"+$seconds;
return $hours.":".$minutes.":".$seconds;
}
So now my average numbers have changed and I'm trying to check the totals to see if they're correct. But I've posted that as another question, located here.
I need to find date x such that it is n working days prior to date y.
I could use something like date("Y-m-d",$def_date." -5 days");, but in that case it wont take into consideration the weekend or off-date. Let's assume my working days would be Monday to Saturday, any idea how I can accomplish this?
Try this
<?php
function businessdays($begin, $end) {
$rbegin = is_string($begin) ? strtotime(strval($begin)) : $begin;
$rend = is_string($end) ? strtotime(strval($end)) : $end;
if ($rbegin < 0 || $rend < 0)
return 0;
$begin = workday($rbegin, TRUE);
$end = workday($rend, FALSE);
if ($end < $begin) {
$end = $begin;
$begin = $end;
}
$difftime = $end - $begin;
$diffdays = floor($difftime / (24 * 60 * 60)) + 1;
if ($diffdays < 7) {
$abegin = getdate($rbegin);
$aend = getdate($rend);
if ($diffdays == 1 && ($astart['wday'] == 0 || $astart['wday'] == 6) && ($aend['wday'] == 0 || $aend['wday'] == 6))
return 0;
$abegin = getdate($begin);
$aend = getdate($end);
$weekends = ($aend['wday'] < $abegin['wday']) ? 1 : 0;
} else
$weekends = floor($diffdays / 7);
return $diffdays - ($weekends * 2);
}
function workday($date, $begindate = TRUE) {
$adate = getdate($date);
$day = 24 * 60 * 60;
if ($adate['wday'] == 0) // Sunday
$date += $begindate ? $day : -($day * 2);
return $date;
}
$def_date="";//define your date here
$preDay='5 days';//no of previous days
date_sub($date, date_interval_create_from_date_string($preDay));
echo businessdays($date, $def_date); //date prior to another date
?>
Modified from PHP.net
Thanks for the help guys, but to solve this particular problem I wrote a simple code:
$sh_padding = 5; //No of working days to count backwards
$temp_sh_padding = 1; //A temporary holder
$end_stamp = strtotime(date("Y-m-d", strtotime($date_format)) . " -1 day"); //The date(timestamp) from which to count backwards
$start_stamp = $end_stamp; //start from same as end day
while($temp_sh_padding<$sh_padding)
{
$sh_day = date('w',$start_stamp);
if($sh_day==0){ //Skip if sunday
}
else
{
$temp_sh_padding++;
}
$start_stamp = strtotime(date("Y-m-d",$start_stamp)." -1 day");
}
$sh_st_dte = date("Y-m-d",$start_stamp); //The required start day
A quick bit of googling got me to this page, which includes a function for calculating the number of working days between two dates.
It should be fairly trivial to adjust that concept to suit your needs.
Your problem, however, is that the concept of "working days" being monday to friday is not universal. If your software is only ever being used in-house, then it's okay to make some assumptions, but if it's intended for use by third parties, then you can't assume that they'll have the same working week as you.
In addition, public holidays will throw a big spanner in the works, by removing arbitrary dates from various working weeks throughout the year.
If you want to cater for these, then the only sensible way of doing it is to store the dates of the year in a calendar (ie a big array), and mark them individually as working or non-working days. And if you're going to do that, then you may as well use the same mechanism for weekends too.
The down-side, of course, is that this would need to be kept up-to-date. But for weekends, at least, that would be trivial (loop through the calendar in advance and mark weekend days where date('w')==0 or date('w')==6).
I have this function that gives me a set of options in a select input.
The options give me times with 5 minute interval.
The problem is when the time is like 23:45, the options will start from 00:10 and loops based on the $j variable.
This is what I want to do in words:
Give me a list of options in 5 minutes interval from $open_time till $close_time.
If the current Time ($timeNow) is greater than the $open_time, set the $open_time to the $timeNow to be shown as first option.
Do this loop only until the $close_time.
I hope this is clear.
Appreciate your help :)
Here is the code:
function selectTimesofDay(){
$output = "";
$now = date('G:i ', time()); // time now
$timeNow = strtotime($now); // strtotime now
$next_five = ceil($timeNow / 300) * 300; // get next 5 minute
// time now rounded to next 10 minute
$round5minNow = date('G:i', strtotime('+15 minutes',$next_five));
$open_time = strtotime('17:00');
$close_time = strtotime('23:59');
// in the middle of working hours, time sets to current
if($timeNow >= $open_time && $timeNow < $close_time){
$open_time = strtotime($round5minNow);
}
$time_diff = round(($close_time - $open_time)/60) ;
if(date('l') == 'Friday'){
$j = ($time_diff/5)+11; // working hours extended untill 1:00 AM
} else{
$j = ($time_diff/5)-1; // working hours untill 12:00 AM
}
for($i = 0; $i <= $j; $i++){
$b = $i*5;
$data = date('l')." - ".date("H:i", strtotime('+'.$b.' minutes', $open_time));
$output .= "<option value=\"{$data}\">";
$output .= $data;
$output .= "</option>";
}
return $output;
}
What you really need is:
function selectTimesOfDay() {
$open_time = strtotime("17:00");
$close_time = strtotime("23:59");
$now = time();
$output = "";
for( $i=$open_time; $i<$close_time; $i+=300) {
if( $i < $now) continue;
$output .= "<option>".date("l - H:i",$i)."</option>";
}
return $output;
}
So what this does is run a loop checking every five-minute interval between opening and closing. Skip it if it is before the curent time, and otherwise add an option.
It's much more efficient than what you were trying to do, and probably easier to understand too.
You can even put this after the loop:
if( $output == "") return "<option disabled>Sorry, we're closed for today</option>";
Also, notice how I left out the value attribute all the time. That's because in the absence of a value, the option's text is used as a value. Thus this solution avoids needless duplication.
Consider taking the hard-coded open and close times out of the function body. The goal with functions is to write code that you can reuse, so if your hours change then you don't have to change with your function, but rather the arguments that are passed to it.
// sample usage: print '<select>'.selectTimesofDay('17:00', '23:59').'</select>';
function selectTimesofDay($start=false, $end=false, $interval='5 minutes'){
$interval = DateInterval::createFromDateString($interval);
$rounding_interval = $interval->i * 60;
$date = new DateTime(
date('Y-m-d H:i', round(strtotime($start) / $rounding_interval) * $rounding_interval)
);
$end = new DateTime(
date('Y-m-d H:i', round(strtotime($end) / $rounding_interval) * $rounding_interval)
);
$opts = array();
while ($date < $end) {
if ($date->getTimestamp() < time()) {
$date->add($interval);
continue;
}
$data = $date->format('l').' - '.$date->format('H:i');
//$opts[] = '<option value="'.$date->getTimestamp().'">'.$data.'</option>'; // < -- pass the timestamp instead of a string?
$opts[] = '<option>'.$data.'</option>';
$date->add($interval);
}
return count($opts) < 1 ?
'<option value="-1">- closed -</option>' :
implode("\n", $opts);
}
Documentation
PHP's DateTime object - http://www.php.net/manual/en/class.datetime.php
PHP's DateInterval object - http://www.php.net/manual/en/dateinterval.format.php
PHP functions - http://www.php.net/manual/en/functions.user-defined.php
PHP function tutorial - http://www.tizag.com/phpT/phpfunctions.php