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);
}
Related
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.
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.
CONTEXT: My client, a local movie theater, runs a Sunday Matinee Special every other Sunday starting with the SECOND SUNDAY every year. So for this year the dates are 1/11, 1/18, 2/8, 2/22, .... [The only exception is the SUNDAY after their film festival which runs the the THIRD FULL WEEK OF OCTOBER, but automating this single exception is a "would-be-nice" item, not essential.]
MY SKILL LEVEL: BEGINNER (I need your help!) I believe I need to use a combination of mktime() and date() but I don't know how to put it together.
WHAT I'VE TRIED: I suspect the answer is a combination of what I see on these three posts:
(1) a do-while loop to get a specific day of the week from a date range
(2) there may be a shortcut for referencing the second sunday in the ACCEPTED ANSWER here, but I'm not sure this helps
(3) MOST RELEVANT(?): Get the First Sunday of Every Month
END RESULT: I want to display the [Month] and [Day] of the next Sunday Matinee (so I want to find and display the first item in the array AFTER the current date). The text will appear like this: "Next: [Month] [Day]"
Make sense? Let me know if I've forgotten anything.
If it's not too much to ask, please explain your code so I (and others?) can learn from this; but I'd be more than grateful for "merely" a straight-up solution.
Many thanks.
Debra
UPDATE/PROGRESS: This code will get me the array of Sundays:
$startDate = strtotime("second Sunday of ".date('Y')."");
for ($i=0; $i < 27; $i++){
$sundays = date('F j', ($startDate + (($i*14) * 24 * 3600))) . '<br>';
print $sundays;
}
NEXT TO FIGURE OUT: write a statement to find in the array of Sundays the first date after the current date.
This is a pretty manual, procedural solution, but it should work.
<?php
$SECS_PER_DAY = 86400;
# Find the first Sunday on or after a given timestamp
function firstSundayOnOrAfter($time) {
global $SECS_PER_DAY;
# What day of the week is the given date?
$wday = date('w', $time);
if ($wday == 0) {
# it's already a Sunday
return $time;
}
return $time + (7 - $wday) * $SECS_PER_DAY;
}
# return an array of timestamps representing
# (noon on) the special matinee Sundays for the given year
function specialMatineeSundays($year) {
global $SECS_PER_DAY;
# When's the film festival?
$oct1 = mktime(12,0,0,10,1,$year);
$festivalStart = firstSundayOnOrAfter($oct1);
$festivalSkip = $festivalStart + 7 * $SECS_PER_DAY;
# find the first Sunday of the year
$jan1 = mktime(12,0,0,1,1,$year);
$sunday = firstSundayOnOrAfter($jan1);
# Add a week to start with the second Sunday
$sunday += 7 * $SECS_PER_DAY;
# Build up our result list
$result = [];
# As long as the Sunday under examination is still the same year,
# add it to the list (unless it's the post-festival skip date)
# and then add two weeks
while (date('Y',$sunday) == $year) {
if ($sunday != $festivalSkip) {
$result[] = $sunday;
}
$sunday += 14 * $SECS_PER_DAY;
}
return $result;
}
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>';
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;
}
?>