Define the due date from a ticket creation date php - php

I am building a SLA KPI.
The SLA is based on ticket type and priority, each of these have a due date.
The thing is, I only have the field created_at, I don't have this due date, so I need to calculate it, and as mentioned above, use the params priority and type to define the right date.
But my real problem is, how to calculate it on PHP, considering operation days and hours, monday to friday, 8:00 to 18:00
For example.
The ticket was created at 2019-06-04 08:00:00 and its deadline is 16 hours.
So it would be, 2019-06-06 08:00:00

After digging a lot looking for a answer, I've found a function that solved my problem.
function addRollover($givenDate, $addtime, $dayStart, $dayEnd, $weekDaysOnly) {
//Break the working day start and end times into hours, minuets
$dayStart = explode(',', $dayStart);
$dayEnd = explode(',', $dayEnd);
//Create required datetime objects and hours interval
$datetime = new DateTime($givenDate);
$endofday = clone $datetime;
$endofday->setTime($dayEnd[0], $dayEnd[1]); //set end of working day time
$interval = 'PT'.$addtime.'H';
//Add hours onto initial given date
$datetime->add(new DateInterval($interval));
//if initial date + hours is after the end of working day
if($datetime > $endofday)
{
//get the difference between the initial date + interval and the end of working day in seconds
$seconds = $datetime->getTimestamp()- $endofday->getTimestamp();
//Loop to next day
while(true)
{
$endofday->add(new DateInterval('PT24H'));//Loop to next day by adding 24hrs
$nextDay = $endofday->setTime($dayStart[0], $dayStart[1]);//Set day to working day start time
//If the next day is on a weekend and the week day only param is true continue to add days
if(in_array($nextDay->format('l'), array('Sunday','Saturday')) && $weekDaysOnly)
{
continue;
}
else //If not a weekend
{
$tmpDate = clone $nextDay;
$tmpDate->setTime($dayEnd[0], $dayEnd[1]);//clone the next day and set time to working day end time
$nextDay->add(new DateInterval('PT'.$seconds.'S')); //add the seconds onto the next day
//if the next day time is later than the end of the working day continue loop
if($nextDay > $tmpDate)
{
$seconds = $nextDay->getTimestamp()-$tmpDate->getTimestamp();
$endofday = clone $tmpDate;
$endofday->setTime($dayStart[0], $dayStart[1]);
}
else //else return the new date.
{
return $endofday;
}
}
}
}
return $datetime;
}
Thank you all.

Related

PHP, repeating events check next one with unix timestamps

I have a problem with php that i don't really know how to solve. I have an array full of unix timestamps coming from a mysql query.
These timestamps are events that repeat every week ( For example, every Tuesday and Thursday ). They can repeat various days or just one.
Knowing the days that repeat, which day will be the next one.
For example:
In the Array I have :
1595289600 --> 2020/07/21 (Tuesday)
1595116800 --> 2020/07/19 (Sunday)
Today we are at 1595376000 (Wednesday) , so it should return 1595116800 + 604800 (Sunday).
In 5 days ( next monday) it should return 1595289600 + 604800 = 1595721600 (First tuesday + one week )
in one week (next Wednesday) , it should return the next Sunday (2020/08/02 ): 1596326400
And so on...
Thank you!
For every timestamp you have - calculate next timestamp (add a week) until it is after current timestamp. Then return lowest from those as that one will be the closest to now (but also in the future).
So lets say it is 2020-07-22 Wednesday.
Your 2020-07-21 Tuesday is in the past, so add a week: 2020-07-28 Tuesday - its in the future, so its our candidate.
Your 2020-07-19 Sunday is also in the past, so add a week: 2020-07-26 Sunday - its in the future so its out second candidate.
Now pick lower from 2 candidates: 2020-07-26 Sunday.
If the dates are more in the past then you will need more a week to them more times.
Something like this:
<?php
// those are your timestamps: $timestamps = [1595289600, 1595116800];
// $time is optional int for when you want to perform the calculation. defaults to current timestamp
function nextOccurence(array $timestamps, $time = null) {
$now = $time ?? time();
$nextTimestamps = [];
foreach ($timestamps as $timestamp) {
while ($timestamp < $now) {
$timestamp += 604800;
}
$nextTimestamps[] = $timestamp;
}
return min($nextTimestamps);
}

Pick a specific day of the week and start counting forward until a set limit is reached in php

Having a php challenge with dynamically setting a day of the week say -thursday, to a function and have the function loop through each day (friday, saturday.. tuesday until a condition i>0 is met.
I have searched thoroughly but could not finding a matching solution.
Please see break down below:
My form:
showing user input that's counted
My code:
//get duraiton in days
function durationInDays($data) {
$startDate = date_create($data['startdate']);
$endDate = date_create($data['enddate']);
$dateDiff = date_diff($startDate,$endDate);
$durationInDays = $dateDiff -> format("%a");
return $durationInDays;
}
//pass duration in days to function to break into weeks and days
function numberOfWeeksAndDays($durationInDays) {
$days = $numdays%7;
$weeks = floor($numdays/7);
return [$weeks, $days];
}
//parse in number of days into function to loop starting from
//day of week of start date say --thursday for example with a
//condition to stop loop once numberOfDays is zero.
function extraMassCount($dayOfWeek, $numberOfDays, $countMass) {
$daysOfTheWeek = [
'monday','tuesday','wednesday','thursday','friday','saturday','sunday',
'monday','tuesday','wednesday','thursday','friday','saturday','sunday'
];
$massCountForExtraDays = 0;
for ($i = $numberOfDays; $i > 0; $i--) {
if($daysOfTheWeek[$i]=='monday')
$massCountForExtraDays += $countMass[0];
if($daysOfTheWeek[$i]=='tuesday')
$massCountForExtraDays += $countMass[1];
if($daysOfTheWeek[$i]=='wednesday')
$massCountForExtraDays += $countMass[2];
if($daysOfTheWeek[$i]=='thursday')
$massCountForExtraDays += $countMass[3];
if($daysOfTheWeek[$i]=='friday')
$massCountForExtraDays += $countMass[4];
if($daysOfTheWeek[$i]=='saturday')
$massCountForExtraDays += $countMass[5];
if($daysOfTheWeek[$i]=='sunday')
$massCountForExtraDays += $countMass[6];
}
return $massCountForExtraDays;
}
The problem: If $numberOfDays=6,then i=6 and the function starts with friday which is not the start date.
My question: how do I implement dayOfWeek parameter so the function extraMassCount will start counting dynamically e.g thursday if startdate is thursday and not the way it is hardcoded to start? I hope my question is clear.
That is, as shown in the form, the function is supposed count the number of masses checked per day and add them together. Starting from startdate to the enddate. Once the durationInDays is broken down to weeks and days I need the function to start at the startdate say --wednesday and add countMass(which is a count of the Masses checked by the user with datatype int) for each day onward.. thursday, friday, etc. – I appreciate the help!
Like this, with relative date formats and DateTime class
$Date = new DateTime;
//go to monday
$Date->modify('monday this week');
for($i=0;$i<10;++$i){
echo $Date->format('l')."\n";
$Date->modify('+1 days'); //go to next day
}
Output
Monday
Tuesday
Wednesday
Thursday
Friday
Saturday
Sunday
Monday
Tuesday
Wednesday
Sandbox
I think the rest you can work out as I am not really sure what that does.
If you want a number for the day, for like this part $countMass[0]; you can use the w format. But 0 is Sunday, if I remember correctly.
for($i=0;$i<10;++$i){
$Date->modify('+1 days');
//dont assume your inputs will be correct
if(isset($countMass[$Date->format('w')])){
$massCountForExtraDays += $countMass[$Date->format('w')];
}
}
PS. instead of all these separate if statements, which is bad performance wise. I would just use a switch statement, but at the very least connect those with else so they don't all evaluate every time, needlessly. $daysOfTheWeek[$i] can only equal one of those. If you notice above, I just optimized them out.

How to loop over weeks and find the exact date of some days?

I'm working on a website where the user can create some events every X days (where X is the name of a day in the week). Then, he needs to enter the number of events he wants to create in the future.
For example, the user selects every Monday and Tuesday and decides to create 150 events.
Here is the code I have made until now :
// Init the date counter
$cpt_date_found = 0;
// Number of date to find
$rec_occ = 150;
// Init an ending date far in the future
$endDate = strtotime('+10 years', time());
// Loop over the weeks
for($i = strtotime('Monday', strtotime(date("d.m.Y"))); $i <= $endDate; $i = strtotime('+1 week', $i)) {
// -- Monday date found, create the event in the database
$cpt_date_found++;
// Break the loop if we have enough dates found
if($cpt_date_found == $rec_occ) {
break;
}
}
This code finds the date of every Monday in the future and breaks the loop once we have reached the number of occurrences the user specified.
I have entered an ending date far in the future to make sure I can break the loop before the end of the occurrences count specified by the user.
First I'm not sure about the "quality" of my code... I know that breaking the loop is not the best idea and am wondering if another solution would better fit my needs.
Then, instead of repeating the loop more times if the user specified several days (let's say, Monday, Tuesday and Friday), is there a way to loop one time for every provided days?
Thanks!
The following code will loop over a period of 5 years. For each week in those 5 years it will generate a DatePeriod containing each day of that week. It will compare each of those days to your preset array with days you are looking for. You can then generate your event after which the code will countdown for a certain amount of times. If the counter hits zero, you are done.
$searchDates = array('Mon', 'Tue', 'Fri');
$amountOfTimes = 27;
$startDate = new DateTime();
$endDate = new DateTime('next monday');
$endDate->modify('+5 years');
$interval = new DateInterval('P1W');
$dateRange = new DatePeriod($startDate, $interval ,$endDate);
// Loop through the weeks
foreach ($dateRange as $weekStart) {
$weekEnd = clone $weekStart;
$weekEnd->modify('+6 days');
$subInterval = new DateInterval('P1D');
// Generate a DatePeriod for the current week
$subRange = new DatePeriod($weekStart, $subInterval ,$weekEnd);
foreach ($subRange as $weekday) {
if (in_array($weekday, array('Mon', 'Fri', 'Sun'))) {
// Create event
// Countdown
$amountOfTimes--;
}
if ($amountOfTimes == 0) {
break;
}
}
}

DateTime class create a function to add hours onto a deadline taking into account working hours and on hold time

Basically I am trying to create a basic case logging system and when somebody opens a new case, the case is assigned a priority with a given number of hours. For example priority 1 is 4 hours, 2 is 9 hours, 3 is 36hours and 4 is 63 hours.
Now adding hours onto a time stamp is easy but the catch is I need to take into account working hours which are 08:30 to 17:30 Monday to Friday. So if a case is given a 4 hour priority and the deadline for this falls after 17:30 on a week day then the deadline is extended to the next working day. Basically the deadline is 4 working hours.
Example:
Case created on: 19/05/2014 16:55 - with Priority 1 (4 Hours)
Deadline is now: 20/05/2014 11:55
Make sense?
Another catch is I need to take into account hours On Hold and these two have to be only within working hours. But ill worry about that later.
I am trying to use the modern DateTime class for this as far as possible.
Here is what I have so far, I am struggling with the logic. It works if the deadline is within a day but if I add something like 63 hours it returns back on a Sunday.
Where am I going wrong here? Brain is fried with this :(
<?php
function addRollover($givenDate, $addtime) {
$datetime = new DateTime($givenDate);
$datetime->modify($addtime);
if (in_array($datetime->format('l'), array('Sunday','Saturday')) ||
17 < $datetime->format('G') ||
(17 === $datetime->format('G') && 30 < $datetime->format('G'))
) {
$endofday = clone $datetime;
$endofday->setTime(17,30);
$interval = $endofday->diff($datetime);
$datetime->add(new DateInterval('P1D'));
if (in_array($datetime->format('l'), array('Saturday', 'Sunday'))) {
$datetime->modify('next Monday');
}
$datetime->setTime(8,30);
$datetime->add($interval);
}
return $datetime;
}
//this is returning back 2014-06-29 17:41:00 which is a sunday.
$future = addRollover('2014-06-26 11:41:00', '+63 hours');
echo $future->format('Y-m-d H:i:s');
See it in action: http://3v4l.org/Ac8ro
Here's an explanation of what's supposed to be going on in the function:
First we create a DateTime object representing our starting date/time
We then add the specified amount of time to it (see Supported Date and Time Formats)
We check to see if it is a weekend, after 6PM, or in the 5PM hour with more than 30
minutes passed (e.g. after 5:30PM)
If so we clone our datetime object and set it to 5:30PM
We then get the difference between the end time (5:30PM) and the modified time as a DateInterval object
We then progress to the next day
If the next day is a Saturday we progress to the next day
If the next day is a Sunday we progress to the next day
We then set our time to 8:30AM
We then add our difference between the end time (5:30PM) and the modified time to our datetime object
We return the object from the function
UPDATE 2
Updated the code as per based on suggestions provided
<?php
function addRollover($givenDate, $addtime, $dayStart, $dayEnd, $weekDaysOnly) {
//Break the working day start and end times into hours, minuets
$dayStart = explode(',', $dayStart);
$dayEnd = explode(',', $dayEnd);
//Create required datetime objects and hours interval
$datetime = new DateTime($givenDate);
$endofday = clone $datetime;
$endofday->setTime($dayEnd[0], $dayEnd[1]); //set end of working day time
$interval = 'PT'.$addtime.'H';
//Add hours onto initial given date
$datetime->add(new DateInterval($interval));
//if initial date + hours is after the end of working day
if($datetime > $endofday)
{
//get the difference between the initial date + interval and the end of working day in seconds
$seconds = $datetime->getTimestamp()- $endofday->getTimestamp();
//Loop to next day
while(true)
{
$endofday->add(new DateInterval('PT24H'));//Loop to next day by adding 24hrs
$nextDay = $endofday->setTime($dayStart[0], $dayStart[1]);//Set day to working day start time
//If the next day is on a weekend and the week day only param is true continue to add days
if(in_array($nextDay->format('l'), array('Sunday','Saturday')) && $weekDaysOnly)
{
continue;
}
else //If not a weekend
{
$tmpDate = clone $nextDay;
$tmpDate->setTime($dayEnd[0], $dayEnd[1]);//clone the next day and set time to working day end time
$nextDay->add(new DateInterval('PT'.$seconds.'S')); //add the seconds onto the next day
//if the next day time is later than the end of the working day continue loop
if($nextDay > $tmpDate)
{
$seconds = $nextDay->getTimestamp()-$tmpDate->getTimestamp();
$endofday = clone $tmpDate;
$endofday->setTime($dayStart[0], $dayStart[1]);
}
else //else return the new date.
{
return $endofday;
}
}
}
}
return $datetime;
}
$currentTime = '2014-06-27 08:30:00';
$dayStart = '8,30';
$dayEnd = '17,30';
$future = addRollover($currentTime, 65, $dayStart, $dayEnd, true);
echo "Results: </br>";
echo $future->format('Y-m-d H:i:s').'</br>';

Converting user's day and time to server's day and time in php

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;
}
?>

Categories