I'm currently working with a data set containing a start time, date, and a duration in seconds, and need to be able to split the range of times from start time to (start time + duration, e.g. end time) into hour "buckets" if you will.
So for instance, a start time of 08:30:00 with duration 9000 seconds (2.5 hours) covers hour 08 from 8:30:00 to 09:00:00 for 1800 seconds, hour 09 until 10:00:00 for 3600 seconds, etc. up to the end time of 11:00:00.
What would be the best way possible to do this? I'm working in PHP however a general algorithmic solution would still be incredibly helpful.
My current approach is as follows:
1. Calculate the hour difference between both times (round up to next hour if there's a non-zero number of minutes)
2. Let session progress = 0
3. Iterate the range from 1 to hour difference:
1. Let the current bucket duration = min(3600, duration)
2. Let the duration = max(0, duration - current bucket duration)
3. Let bucket start time = start time + session progress
4. Let bucket end time = bucket start time + current bucket duration
5. Do work based on these values
6. Let session progress += current bucket duration
My PHP implementation of this is as follows:
foreach ($csv as $listen_record) {
$start_dt = $listen_record['Date'] . ' ' . $listen_record['Time'];
$session_duration = $listen_record['Duration'];
$session_start_time = date_create_from_format($datetime_format, $start_dt);
$session_end_time = clone $session_start_time;
date_add($session_end_time, date_interval_create_from_date_string($session_duration . ' seconds'));
$diff = date_diff($session_end_time, $session_start_time);
$hourdiff = $diff->h;
if ($diff->m > 0)
$hourdiff += 1;
$session_progress = 0;
foreach (range(1, $hourdiff) as $hour) {
$record_duration = min(3600, $session_duration);
$session_duration = max(0, $session_duration - $record_duration);
$record_start_time = clone $session_start_time;
date_add($record_start_time, date_interval_create_from_date_string($session_progress . ' seconds'));
$record_end_time = clone $record_start_time;
date_add($record_end_time, date_interval_create_from_date_string($record_duration . ' seconds'));
if ($record_start_time == $record_end_time)
continue;
// DO WORK...
$session_progress += $record_duration;
}
}
This kind of works to separate each record into hour long buckets however gives some weird results for some cases (particularly cases where the range crosses into a new day) and doesn't align to actual wall clock hours.
Is there a better way to do this that can actually align to wall clock hours and not freak out when it has to cope with durations that cross over midnight?
Thanks!
Update: I managed to solve this on my own so I'm posting how I did it just in case anyone else needs to do similar.
Basically I did it by creating a range between the start and end timestamp, and filtering out every time stamp apart from start and end that isnt a multiple of 3600 or just before 3600. This gives me the wall clock hours in the middle of the range as timestamps. I can then split the array into chunks of 2 which contains the slot start time and end time.
My PHP code is as follows and now actually works:
function is_wall_clock_aligned_hour_boundary($timestamp) {
return ($timestamp % (60*60) === 0) || (($timestamp + 1) % (60*60) === 0);
}
function create_slot_object($slot) {
$start_time = reset($slot);
$end_time = end($slot);
$duration = $end_time - $start_time;
return array(
'startTime' => convert_timestamp_to_datetime($start_time),
'endTime' => convert_timestamp_to_datetime($end_time),
'duration' => $duration
);
}
function make_slots(DateTime $start_time, $duration) {
$start_timestamp = $start_time->getTimestamp();
$end_time = clone $start_time;
date_add($end_time, date_interval_create_from_date_string("$duration seconds"));
$end_timestamp = $end_time->getTimestamp();
$time_sequence = range($start_timestamp, $end_timestamp);
$slot_boundaries = array_filter($time_sequence, 'is_wall_clock_aligned_hour_boundary');
array_unshift($slot_boundaries, $start_timestamp);
array_push($slot_boundaries, $end_timestamp);
$slots = array_chunk($slot_boundaries, 2);
return array_map('create_slot_object', $slots);
}
Related
I want to show time slots between 2 times. Start time & End time. Used below code, it's working fine. It loops start and end time and add 30 mins to time. Starts from 2:00pm, 2:30pm, 3:00pm all the way till 10:00pm
$start_time = "14:00:00";
$end_time = "22:30:00";
#for ($i = strtotime($start_time); $i < strtotime($end_time); $i=$i+1800)
<li data-time="{{date('g:i A', $i)}}" class="timefield">{{date("g:i A", $i)}}</li>
#endfor
What I am stuck on is 2 parts
Hide past time, lets say right now is 4:00pm, it should hide past time slots i-e 2:00pm,2:30pm,:3:00pm,3:30pm
If right now is 4:00pm, it should start from 5:00pm all the way till 10:00pm. Adding extra buffer time of 1 hour.
You could insert the current timestamp in your logic like this:
$start_time = strtotime("14:00:00");
$end_time = strtotime("22:30:00");
$now = (new DateTime())->getTimestamp();
$nowRemaining = $now % 1800; // Divide to half hours & get the remaining seconds
$nowRounded = $now - $nowRemaining; // Round to half hours
$nextHour = $nowRounded + ($nowRemaining == 0 ? 3600 : 5400); // Add the extra buffer
for ($i = max($start_time, $nextHour); $i < $end_time; $i=$i+1800) {
...
}
I am making a ticketing system for my company. In my database I record the timestamp of when a ticket is first raised and a timestamp of when the ticket is marked as completed.
I have written a function which returns the average time (hrs) a ticket takes to complete:
public function calculateAvgResolveTime()
{
$timeQuery = $this->database->query('SELECT ticketCreated, ticketCompletedOn FROM employeeTickets');
$cumulativeTicketTime = $cumulativeTimes = 0;
while($time = $timeQuery->fetch_assoc()) {
$timeCreated = strtotime($time['ticketCreated']);
$timeCompleted = strtotime($time['ticketCompletedOn']);
if($timeCompleted > $timeCreated) {
$cumulativeTimes++;
$cumulativeTicketTime = $cumulativeTicketTime + ($timeCompleted - $timeCreated);
}
}
$time = ($cumulativeTicketTime / 60 / 60);
$time = sprintf('%02d:%02d', (int) $time, round(fmod($time, 1) * 60));
return $time;
}
Is there a way I could exclude certain hours? For example our office is open from 09:00-17:00 Monday to Friday.
At the moment if a ticket is raised at 16:30 on a Friday and is completed 09:15 on Monday the average time would be quite high when actually the ticket only took 45 minutes of working time.
Result of var_export():
array(
array ( 'ticketCreated' => '2020-02-03 15:59:30','ticketCompletedOn' => '2020-02-04 09:53:35'),
array ( 'ticketCreated' => '2020-02-04 14:00:00', 'ticketCompletedOn' => '2020-02-04 14:36:00')
)
You will have to loop over the dates between ticketCreated and ticketCompletedOn day by day.
There seems to be no mathy way(or at least not in readable format) to solve this as you have time constraints of excluding Saturdays and Sundays as well as the working period being from 09:00:00 to 17:00:00.
Snippet:
<?php
$data =array(
array ( 'ticketCreated' => '2020-02-03 15:59:30','ticketCompletedOn' => '2020-02-04 09:53:35'),
array ( 'ticketCreated' => '2020-02-04 14:00:00', 'ticketCompletedOn' => '2020-02-04 14:36:00')
);
$sum_time = 0;
foreach($data as $details){
$start_time = new DateTime($details['ticketCreated']);
$end_time = new DateTime($details['ticketCompletedOn']);
$end_of_day = new DateTime($start_time->format('Y-m-d') . ' 17:00:00'); // since a day ends at 17:00
do{
$diff = $end_time->diff($start_time);
$diff2 = $end_of_day->diff($start_time);
if($end_time->format('Y-m-d') === $start_time->format('Y-m-d')){ // meaning finished on the same day
$sum_time += ($diff->h * 60) + ($diff->i) + ($diff->s / 60);
}else if(!in_array($end_of_day->format('N'),[6,7])){ // skipping Saturdays and Sundays
$sum_time += ($diff2->h * 60) + ($diff2->i) + ($diff2->s / 60); // add the day's offset(480 minutes)
}
$end_of_day->add(new DateInterval('P1D'));
$start_time = new DateTime($end_of_day->format('Y-m-d') . ' 09:00:00'); // start time for next day which is 09:00
}while($start_time <= $end_time);
}
$avg = $sum_time / count($data);
echo "$avg minutes",PHP_EOL;
Demo: https://3v4l.org/gpFt4
Explanation:
We first create DateTime instances of the dates.
We now have a do while loop inside.
If the end time and start time fall on the same day, we just take differences in terms of hours, minutes and seconds.
If the end time and start time doesn't fall on the same day, then we subtract the times from start_time from end_of_day which will be 480 minutes for a proper start or remaining offset of that day till 17:00:00.
If we come across a day which is Saturday or Sunday, we just skip it.
In the end, we just print the average by dividing sum by total number of tickets.
How can I calculate the nearest hours to midnight time 00:00 regardless of date in PHP. For example:
If time is 22:00 then 2 hours are required to reach 00:00
If time is 04:00 then -4 hours are the nearest to reach 00:00
Currently I have the following PHP function:
<?php
$ts1 = strtotime('00:00');
$ts2 = strtotime('04:00');
$diff = ($ts1 - $ts2) / 3600;
?>
But this won't be helpful much in the above.
If you have the php Datetime class available you can calculate the difference between two DateTimes.
$time1 = new \DateTime('00:00');
$time2 = new \DateTime('04:00');
$diff = $time1->diff($time2, true);
$hourDifference = 0;
if ($diff->h < 12) {
$hourDifference = -$diff->h;
} elseif ($diff->h > 12) {
$hourDifference = 24 - $diff->h;
} else {
$hourDifference = 12; // kann be positive or negative
}
And you'll get a DateInverall object where you can access, hours, minuts, seconds and compare them with normal php operators.
If you'r not too interested in minutes;
1. Extract minutes.
check if minutes is > or <=30
if greater, 'store' 1
2. Extract hour
check if hour is greater than 12
if not, add 12 (store flag also to say it will be minus)
3. if greater (ref. Step 1), add 1 to extracted hour.
4. 24 - extracted hour is your interval.
Please note, this may be reduced/ simplified greatly.
Your interval (should) be correct to the nearest half hour
The answer depends on the date (not only the time). This is because of daylight saving time changes. For example might 02:59 being closer to 00:00 then 21:01 on the time where daylight saving time will set back hour.
I have a scenario in which the user selects a time and day (or multiple days) and that value must be converted to whatever that day and time would be in UTC time. I have the gmt offset amount for each user (the users set it when they signup). For instance:
A user in the eastern timezone selects:
3:15 pm, Monday, Tuesday, Friday
I need to know what time and days that information would be in UTC time. The solution has to take into situations such Monday in one timezone can be a different day in UTC time. Also, if the time can be converted to 24 hour format, that would be a plus.
For the sake of clarity, something along the lines of an array should be returned such as:
Array('<3:15 pm eastern adjusted for utc>', '<Monday adjusted for UTC>', '<Tuesday adjusted for UTC>', '<Friday adjusted for UTC>');
I don't need the result to be directly formatted into an array like that - that's just the end goal.
I am guessing it involves using strtotime, but I just can't quite my finger out how to go about it.
$timestamp = strtotime($input_time) + 3600*$time_adjustment;
The result will be a timestamp, here's an example:
$input_time = "3:15PM 14th March";
$time_adjustment = +3;
$timestamp = strtotime($input_time) + 3600*$time_adjustment;
echo date("H:i:s l jS F", $timestamp);
// 16:15:00 Monday 14th March
EDIT: kept forgetting little things, that should be working perfectly now.
Made a function to do the job:
<?
/*
* The function week_times() converts a a time and a set of days into an array of week times. Week times are how many seconds into the week
* the given time is. The $offset arguement is the users offset from GMT time, which will serve as the approximation to their
* offset from UTC time
*/
// If server time is not already set for UTC, uncomment the following line
//date_default_timezone_set('UTC');
function week_times($hours, $minutes, $days, $offset)
{
$timeUTC = time(); // Retrieve server time
$hours += $offset; // Add offset to user time to make it UTC time
if($hours > 24) // Time is more than than 24 hours. Increment all days by 1
{
$dayOffset = 1;
$hours -= 24; // Find out what the equivelant time would be for the next day
}
else if($hours < 0) // Time is less than 0 hours. Decrement all days by 1
{
$dayOffset = -1;
$hours += 24; // Find out what the equivelant time would be for the prior day
}
$return = Array(); // Times to return
foreach($days as $k => $v) // Iterate through each day and find out the week time
{
$days[$k] += $dayOffset;
// Ensure that day has a value from 0 - 6 (0 = Sunday, 1 = Monday, .... 6 = Saturday)
if($days[$k] > 6) { $days[$k] = 0; } else if($days[$k] < 0) { $days[$k] = 6; }
$days[$k] *= 1440; // Find out how many minutes into the week this day is
$days[$k] += ($hours*60) + $minutes; // Find out how many minutes into the day this time is
}
return $days;
}
?>
Suppose the target time is 4.30 pm and the current time is 3.25 pm , how will i calculate the minutes remaining to reach the target time ? I need the result in minutes.
session_start();
$m=30;
//unset($_SESSION['starttime']);
if(!$_SESSION['starttime']){
$_SESSION['starttime']=date('Y-m-d h:i:s');
}
$stime=strtotime($_SESSION['starttime']);
$ttime=strtotime((date('Y-m-d h:i:s',strtotime("+$m minutes"))));-->Here I want to calcuate the target time; the time is session + 30 minutes. How will i do that
echo round(abs($ttime-$stime)/60);
Krishnik
A quick calculation of the difference between two times can be done like this:
$start = strtotime("4:30");
$stop = strtotime("6:30");
$diff = ($stop - $start); //Diff in seconds
echo $diff/3600; //Return 2 hours. Divide by something else to get in mins etc.
Edit*
Might as well add the answer to your problem too:
$start = strtotime("3:25");
$stop = strtotime("4:30");
$diff = ($stop - $start);
echo $diff/60; //Echoes 65 min
Oh and one more edit:) If the times are diffent dates, like start is 23:45 one day and end is 0:30 the next you need to add a date too to the strtotime.