PHP Check for time differences - php

I'm looking for a lean way to compare two times. One is the time, someone requested a new password(FOS UserBundle provides a Getter for that) and the other is the current time minus e.g. 10 minutes.
if($user->getPasswordRequestedAt() <= date("Y-m-d H:i:s", strtotime('-10 minutes'))){
return $this->render('#User/Resetting/no_resend.html.twig', array( 'username' => $username));
}
else {
return $this->render('#User/Resetting/check_email.html.twig', array(
'tokenLifetime' => ceil($this->container->getParameter('fos_user.resetting.retry_ttl') / 60),
));
So if someone requested a password already 10 minute ago, he gets to the page saying "You already requested a password, please wait 10 minute to retry.". If the request was longer than 10 minutes ago, the pages says "Email for password reset has been sent".
I would think the comparison is right that way but it's somehow always going to the "else" part.
Where's the mistake?

if($user->getPasswordRequestedAt() >= date("Y-m-d H:i:s", strtotime('-10 minutes'))){
Should do the trick.
Since you want to check if the last request was done in the last 10 minutes or newer

As far as I know $user->getPasswordRequestedAt() (since being processed through Doctrine) is an instance of \DateTime.
So use another datetime to compare it:
$limit = \new DateTime();
$limit->sub(new \DateInterval("P600S"));
if($user->getPasswordRequestedAt() >= $limit){
return $this->render('#User/Resetting/no_resend.html.twig', array( 'username' => $username));
}
else {
return $this->render('#User/Resetting/check_email.html.twig', array(
'tokenLifetime' => ceil($this->container->getParameter('fos_user.resetting.retry_ttl') / 60),
));
With this you keep the OOP style and have a clean if clause. The $limit holds a timestamp from 10 minutes ago. So when you requested your password at 14:10 and it is 14:19 by now, $limit will hold 14:09. As long as the requested password date is bigger than the timestamp 10 minutes ago, there won't be a resend.
EDIT: Since you are using the FOS UserBundle it is indeed possible to make it even shorter as iainn pointed out:
if($user->isPasswordRequestNonExpired(600)){
return $this->render('#User/Resetting/no_resend.html.twig', array( 'username' => $username));
}
else {
return $this->render('#User/Resetting/check_email.html.twig', array(
'tokenLifetime' => ceil($this->container->getParameter('fos_user.resetting.retry_ttl') / 60),
));
The first and only argument of this method is a amount of seconds that have to pass so that the password can be requested again.

Since I haven't used symfony I can't know for sure.
But this is my guess. See if it works.
$dt = $user->getPasswordRequestedAt();
If($dt->getTimestamp() <= strtotime(time())-600){
return $this->render('#User/Resetting/no_resend.html.twig', array( 'username' => $username));
}else {
return $this->render('#User/Resetting/check_email.html.twig', array(
'tokenLifetime' => ceil($this->container->getParameter('fos_user.resetting.retry_ttl') / 60),
));
This will create the datetime object to $dt variable.
In the if() I use get_timestamp to get the Unix value of the datetime object and compare it to Unix time -600 seconds (10 minutes).
The reason I choose Unix values is because they are great for comparison.

Related

Normalize DateInterval in PHP

The situation/issue
I have an issue with DateInterval in PHP.
Given the following code
$interval = new DateInterval("PT6000S");
echo $interval->h; // 0
echo $interval->i; // 0
echo $interval->s; // 6000
echo $interval->format("%h:%i"); // 0:0
I want this to represent 1 hour and 40 minutes, not 6000 seconds.
The question
Is there a built-in way to normalize the DateInterval? A specific way of writing the duration string? Or is this something that must be done normally?
I can modify the way it's created and formated if anyone has a "work-around" to suggest.
I have done my research, but strangely enough I have not found anything helpful with this issue.
The DateInterval class is a bit annoying like that. Strictly it's doing the right thing, since your interval spec string specified zero minutes and hours, but it's definitely not helpful that it doesn't roll over excess seconds into minutes and hours.
One option I've used before is to add the interval to a new DateTime object initialised to the Unix epoch, then format the resulting time instead. This would mean using the standard date format strings (so h rather than %h, etc)
echo (new DateTime('#0'))->add(new DateInterval("PT6000S"))->format('h:i');
// 01:40
See https://3v4l.org/OX6JF
The solution
Like every time I ask for help, I cannot stop trying and I generally find the answer right after. No exception this time.
I've found a user contributed note in the DateInterval documentation.
You have to create two DateTime (they can be any date and time), add your interval to the second and substract the first from the second. This will populate the DateInterval in a normalized way.
Here is the code the user wrote, wrapped in a handy-dandy function:
public function createDateInterval(string $duration): DateInterval
{
$d1 = new DateTime();
$d2 = new DateTime();
$d2->add(new DateInterval($duration));
return $d2->diff($d1);
}
(Note that this function can throw an exception if your $duration string is not correctly formatted.)
This solution uses DateTime with a fixed time. Why ?
DateTime also supports microseconds. There is therefore a certain probability that 2 calls to new Date() will not return the same time. Then we can get wrong results.
//360000 Sec = 100 hours = 4 Days + 4 hours
$di = new DateInterval("PT360000S");
$diNormalize = date_create('#0')->diff(date_create('#0')->add($di));
var_export($diNormalize);
/*
DateInterval::__set_state(array(
'y' => 0,
'm' => 0,
'd' => 4,
'h' => 4,
'i' => 0,
's' => 0,
'f' => 0.0,
'weekday' => 0,
'weekday_behavior' => 0,
'first_last_day_of' => 0,
'invert' => 0,
'days' => 4,
'special_type' => 0,
'special_amount' => 0,
'have_weekday_relative' => 0,
'have_special_relative' => 0,
))
*/
echo $diNormalize->format('%a days %h hours %i minutes');
//4 days 4 hours 0 minutes
DateInterval has a special format method for the output. This should be used here and no detour via date, gmdate or DateTime should be taken.

Deducting 24 hours from a timestamp

I am wondering if there is an easy way to do this, i'm pulling a timestamp from mysql, i then want to check if the timestamp is less than 24 hours ago, if it is, i do nothing, else i will do an action, my code:
$dbStoredDate = $theDate['site_date'];
$theTimeMinus = strtotime('-1 day', $dbStoredDate);
if ($theTimeMinus <= $dbStoredDate) {
}
This is what i have come up with, i realize now it would have been better if i used time() instead of timestamp() in mysql, lesson learned, the first value is coming from mysql, the second is just deducting 1 day, does the logic look ok do you think? thanks for any input guys.
You can do this easily (assuming you're using unix timestamp, if not you can convert it):
$continue = ((time() >= ($theDate['site_date']+86400));
if ($continue)
echo "older than/equal to 24 hours";
else
echo "not older than 24 hours";

Laravel - Get data last 24 hours, 1 week or 1 month

So I'm trying to grab only data which are in last 24 hours, 1 week, 1 month. I have working code, but if I get it correctly it's "other way". By that I mean, If date is at the moment 30-Nov-2016 and I set the Data value to 1-Dec-2016, then the 24 Hour one still grabs the information, but it shouldn't. If date is 30-Nov-2016 and I set it to 28-Nov-2016, then the 24 Hour one doesn't grab it. For me It sounds like it's backwards. I hope the explanation is understandable.
$getItemsOneDay = Deposit::where('steam_user_id',0)->where('status', Deposit::STATUS_ACTIVE)->where('created_at', '>', Carbon::now()->subMinutes(1440))->get();
$getItemsOneWeek = Deposit::where('steam_user_id',0)->where('status', Deposit::STATUS_ACTIVE)->where('created_at', '>', Carbon::now()->subMinutes(10080))->get();
$getItemsOneMonth = Deposit::where('steam_user_id',0)->where('status', Deposit::STATUS_ACTIVE)->where('created_at', '>', Carbon::now()->subMinutes(43200))->get();
You wrote created_at > time.
So you ask laravel to search all data greater than now(). So you look into the future.
Either you want to...
look at points before a certain point in time: created_at < time
look at points after a certain point in time: created_at > time
look at points within an interval of passed time: created_at > start && created_at < end
If i got you right you are searching for the third option. So you need to do something like ...->where("created_at",">",Carbon::now()->subDay())->where("created_at","<",Carbon::now())->...
Hopefully, I got you right here.
->where('created_at', '>=', Carbon::now()->subDay()->toDateTimeString())->get();
to get the data created within last 24 hours
Check the below function; it is working fine.
public function getanswer_24hours(Request $request)
{
$ans = Answer::where('user_id',$request->user_id)->where('created_at', '>=', Carbon::now()->subDay()->toDateTimeString())->get();
if(count($ans) > 0)
{
$result = 'yes';
return response()->json(['data' =>['answer' => $ans,'result' => $result, 'message' => 'get Answer Before 24hours SuccessFully!!','status' => true],200,'ok']);
}else{
$result = 'no';
return response()->json(['data' =>['answer' => [], 'result' => $result, 'message' => 'get Answer Failed!!'],404,'no']);
}
}

Selecting upcoming sessions based on UNIX timestamps

I have a service that shows a specific button when a session is "ready", i.e. 15 minutes beforehand and through the session.
I am doing this by pulling up all recently requested sessions, analyzing their timestamps, and then pulling out a specific ID if that session is upcoming. Here is the code:
$session_check_query = "SELECT * FROM requested_sessions WHERE username_t = '{$_SESSION['username']}'";
$session_check_process = mysql_query($session_check_query);
date_default_timezone_set($_SESSION['timezone']);
$current_time = time();
while ($sessions = mysql_fetch_array($session_check_process)) {
if ($sessions['time_from_t'] - $current_time <= 900 && $current_time - $sessions['time_from_t'] > 0 && $sessions['accepted'] == 1) {
$session_id = $sessions['id'];
}
}
The problem is that when I echo $session_id in the loop it will output 1, 2, 3, 4. This means that it feels that all of my sessions in the database meet this criteria when they don't only one does.
Is there a problem with this code? time_from_t is in UNIX format.
One possible problem may arise from the fact that time() is timezone independent - it always returns the UTC time (this fact is buried in the comments). More robust handling of timezones can be handled with the DateTime object:
$date = new DateTime('now', new DateTimeZone($_SESSION['timezone']));
$current_time = $date->format("U");
Either way, the next step would be to verify your $current_time variable and the time_from_t to see where the maths is going wrong.

Working out which data point to increment PHP

I have created an array with 10 timestamps each 1 day apart:
$data_points = array();
$now = time();
$one_day = 60 * 60 * 24;
for($i = 1; $i <= 10; ++$i) {
$key = $now - ($one_day * $i);
$data_points[$key] = 0;
}
print_r($data_points);
Array
(
[1328642414] => 0
[1328556014] => 0
[1328469614] => 0
[1328383214] => 0
[1328296814] => 0
[1328210414] => 0
[1328124014] => 0
[1328037614] => 0
[1327951214] => 0
[1327864814] => 0
)
Now I have a array of tasks that have started at various times in the last 10 days, I want to see which day my task fell into.
I was going to loop through each $data_point and see if the start time is greater than the current day and less than the next, then increment that data point
is there a better way to do this?
Thanks
Well, to reduce your search time you could put your data into a binary search tree rather than a simple array.
Whether or not that's worth the trouble depends on how big your data set is. Of course, you'd also have to re-balance your tree every so often as you add new dates.
I think there's a better method.
Assuming you have task starting timestamps in an array, the algorithm will be something like :
for each task starting timestamp
timestamp <- $now - timestamp // you will obtain task age in seconds
timestamp <- timestamp / (60*60*24) // you will obtain task age in days
// round resulting timestamp with a precision of 0 if you want to obtain the task age in integer days.
end for each
In this way you will loop on only one array. This will be less expensive than your method.
Obviously, if your tasks come from a SQL database, there will be a greater solution in SQL.
You can use DateTime class
$now = new DateTime();
$task = new DateTime('2012-02-20');
$interval = $taks->diff($now);
echo 'Here is the position you need:' . $interval->format('%R%a days');
** Updated to avoid use of DateTime as asked in comment **
$now = date('Ymd');
$task = date('Ymd',$tasktime);
$interval = $task - $now;
The interval is the number you expect.
I know this question is old, but since there are no accepted answers, and it seems like a fun question to answer - here we go!
Based on your question, your algorithm has the Big O of O(10n) where n is the number of tasks. This means, that it is fairly efficient compared to a lot of things. As pointed out, a binary search tree would be faster having O(log(n)), however implementing it wouldn't really be worth the saved time during processing. Though, you can make it slightly more efficient and have a resulting O(n) by using something like:
$now = time();
$oneDay = 86400; //60 * 60 * 24
foreach($tasks as $task) {
//assuming now that $task is the timestamp of the task
//extra paranthesis added for easier reading
$dif = $now - ($oneDay * ceil(($now - $task) / $oneDay));
$data_points[$dif]++;
}
The math in the diff is as follows. $now-$task is the difference between the two timestamps in seconds, we divide by $oneDay to get the number of days in the past the task occurred. Now, assuming that $now is the start of a new day, and if an event happened just 12 hours ago it was 'yesterday', we use ceil to round it to the next integer so '.5' becomes '1'. From there, we multiply by $oneDay to get the number of seconds of the days that have passed - to work with the $data_points array that you previously created. We then take that result and subtract it from $now, again to work with your $data_points array. That result gives us a time stamp that we can use that matches those in the array you created, and we use it as the 'key' for it and increment accordingly.
This will prevent you from having to loop through the entire $data_points array for each task, and thus reducing its complexity from O(10n) to O(n).
Anyways, I hope that answer helps explain why your formula isn't that inefficient, but shows how to make it ever so slightly more efficient.

Categories