I am having a bit of trouble working out how to validate whether a timezone has passed a certain time (local to the time zone).
So for instance, if the time in London has passed 18:00:00
$tz = new DateTimeZone('Europe/London');
$datetime1->setTimezone($tz); // calculates with new TZ now
if ($datetime1->format('H:i:s') >= strtotime('18:00:00')) {
echo "time has passed";
} else {
echo "time has NOT passed";
}
The problem with this is that strtotime('18:00:00') seems to be using the server time.
If I echo strtotime('18:00:00'); will return 1470247200 which is the amount of seconds since 1970 but this will not be the 6pm time for another timezone for instance America/New_York which at the time of writing this has not passed.
Any idea how this can be done?
Thanks,
Use DateTime's own comparison feature since it includes time zone support:
$tz = new DateTimeZone('Europe/London');
$datetime1->setTimezone($tz); // calculates with new TZ now
$datetime2 = new \DateTime('18:00:00', $tz);
if ($datetime1 >= sdatetime2) {
echo "time has passed";
} else {
echo "time has NOT passed";
}
I think this should work:
if ($datetime1->format('H:i:s') >= '18:00:00') {
The left side is a string, and every component contains leading zeros, so you can just do a string comparison with the right side.
(This assumes that you consider midnight of the next day to not have "passed" 18:00:00.)
Related
I'm trying to do a comparison between date & times in Carbon PHP 2. For context, my server is in Europe/London timezone, and a user has the functionality to set their own timezone, thus my $timezone variable. My Laravel 8 project default timezone config is Europe/London too.
When a user provides a start time, I store the date & time as a date field in my DB, but obviously the day, month and year would always be wrong at the point my code runs, thus why we override these with the current day.
Still though, you can see from my output that their time is greater than the start time, but my if statement never runs, why?
$timezone = 'Asia/Tokyo';
$startTime = Carbon::parse('2022-08-01 05:00:00');
$theirTime = Carbon::parse(Carbon::now())->setTimezone($timezone);
$ourTime = Carbon::parse($theirTime)->setTimezone('Europe/London');
$startTime = $startTime->day($theirTime->day);
$startTime = $startTime->month($theirTime->month);
$startTime = $startTime->year($theirTime->year);
echo "their time: $theirTime ----- start: $startTime";
if ($theirTime >= $startTime) {
echo 'run now';
} else {
echo 'do not run';
}
output is:
their time: 2022-08-05 05:16:27 ----- start: 2022-08-05 05:00:00do not run
05:16:27 is greater than 05:00:00 so should output run now, what am I missing?
You can use carbon gte() method
if ($theirTime->gte($startTime)) {
echo 'run now';
} else {
echo 'do not run';
}
as for why it says "do not run", it is because "2022-08-05 05:16:27 GMT+1" comes before "2022-08-05 05:00:00 GMT" and carbon carbon converts itself to integer (unix timestamp) in the comparisation.
I cant give you a complete example because you did not define what $timezone is in your question.
If you want to compare the times as strings directly (and have total faith in your control of timezones you can
if ($theirTime->format('H:i:s') >= $startTime->format('H:i:s')) {
echo 'run now';
} else {
echo 'do not run';
}
Ideally you would run the server in UTC. When the user enters Wake me up at 6am you need to know 6am in what timezone, so need to store the timezone with the 6am or with that user's profile - whatever makes more sense in your application. But it would be a problem to search the database for each user's wakeup time in their own timezone so for activities like this, convert the time to UTC before storing it.
Then if the user wanted waking at 6am, this might be 18:00 utc but that would not matter. When the 'wakeup' time is the same as the server's current time, wake the user up and tell them "this is your $wakeup)->tz($user->timezone) wakeup".
Regarding your specific situation, you want to know if now() in Tokyo is greater than the time on the DB record, however you can only look at the H:m in the stored value;
$theirTime = Carbon\Carbon::parse('2022-03-01 05:00'); // get this from DB
// our reference point
$current = now()->tz('Asia/Tokyo');
$target = now()->tz('Asia/Tokyo');
$target->hour = $theirTime->hour;
$target->minute = $theirTime->minute;
$target->second = 0;
if($current->gte($target)) {
echo 'overdue';
} else {
echo 'Not due';
}
I have these two functions:
function time_is_older_than($timestamp, $time_string)
{
if (strtotime($timestamp) < strtotime('-' . $time_string))
return true;
return false;
}
function time_is_younger_than($timestamp, $time_string)
{
if (strtotime($timestamp) > strtotime('-' . $time_string))
return true;
return false;
}
They enable me to do neat things like:
if (time_is_older_than($last_time_some_action_happened, '5 minutes'))
do_it_again();
They normally work, except for during one hour every six months, when my timezone switches over to "summer time" or "winter time". This means that the clocks are increased or put back one hour at midnight (according to this timezone).
The PHP manual states this for strtotime:
The Unix timestamp that this function returns does not contain information about time zones. In order to do calculations with date/time information, you should use the more capable DateTimeImmutable.
However, if I provide the exact same date/time string, with "+08:00" added in the end versus "+00:00", for example, I get different numbers of seconds returned. So strtotime() does understand timezones when it parses the provided time, even if the returned integer obviously doesn't contain this information. (Nor is it expected or required to by me.)
I've spent countless hours trying to debug this, testing countless things, and just sitting here thinking, but I can't figure out what exactly would make the code I have fail, specifically for one hour. And especially what about it I need to change. Setting the second parameter for strtotime() seems likely, but I just couldn't make it work correctly.
My hottest "lead" for quite some time was that the strtotime('-' . $time_string) part is ending up using a different timezone than the timestamp strings provided, but I do provide timezone data to it most of the time! An example of $last_time_some_action_happened might be something like 2020-10-28 02:22:41.123456+01.
I set the timezone with date_default_timezone_set().
I suspect that I only need to make some very minor change, but I've been experimenting so much and so long now, even taking rests in between, that my brain can no longer see this clearly. I bet the solution is something awfully simple.
Please don't tell me to use DateTimeImmutable. This would fundamentally change my entire structure and require me to do things very differently. Perhaps I should, and even will, at some point, but for now, I just wish to fix this rare but still very annoying bug in my existing code. (If it's possible at all, which I very much believe is the case.)
I'm able to reproduce the issue you are having:
date_default_timezone_set('Pacific/Auckland');
// Daylight saving time 2020 in New Zealand began at 2:00am on Sunday, 27 September
$current = strtotime('2020-09-27 02:04:00');
$d1 = strtotime('2020-09-27 02:05:00', $current);
$d2 = strtotime('-5 minutes', $current);
var_dump($d1 > $d2); // false
var_dump(date('Y-m-d H:i:s', $d1)); // 2020-09-27 03:05:00
var_dump(date('Y-m-d H:i:s', $d2)); // 2020-09-27 03:59:00
This person looks to be having the same issue as you and may appear to be a bug.
DateTime::modify and DST switch
The solution is to convert the dates to UTC then compare:
// Convert to UTC and compare
$d1 = new \DateTime('2020-09-27 02:05:00', new \DateTimeZone('Pacific/Auckland'));
$d2 = new \DateTime('2020-09-27 02:04:00', new \DateTimeZone('Pacific/Auckland'));
$d2->setTimezone(new \DateTimeZone('UTC'));
$d2->modify('-5 minutes');
$d2->setTimezone(new \DateTimeZone('Pacific/Auckland'));
var_dump($d1 > $d2); // true
var_dump($d1->format(\DateTimeInterface::RFC3339_EXTENDED)); // 2020-09-27T03:05:00.000+13:00
var_dump($d2->format(\DateTimeInterface::RFC3339_EXTENDED)); // 2020-09-27T01:59:00.000+12:00
I've updated your functions:
function time_is_older_than($datetime, $time_string)
{
$d1 = new \DateTime($datetime);
$d1->setTimezone(new \DateTimeZone('UTC'));
$d2 = new \DateTime();
$d2->setTimezone(new \DateTimeZone('UTC'));
$d2->modify('-' . $time_string);
return $d1 < $d2;
}
function time_is_younger_than($datetime, $time_string)
{
$d1 = new \DateTime($datetime);
$d1->setTimezone(new \DateTimeZone('UTC'));
$d2 = new \DateTime();
$d2->setTimezone(new \DateTimeZone('UTC'));
$d2->modify('-' . $time_string);
return $d1 > $d2;
}
Could you consider a solution:
In the timestamp string(like Thu, 21 Dec 2000 16:01:07 +0200), add a timezone tag which specify timezone without difference of daylight saving time.
I am working on email link expire after some X minutes where X denotes some random date_time. so my motive is to expire the the link after some time what ever I set the date_time in side the $expire_date.
So I just created dummy code myself just in order to sure my code works or not.
$currentDateTime = new \DateTime();
$currentDateTime-> setTimezone(new \DateTimeZone('Asia/kolkata'));
$now = $currentDateTime-> format(' h:iA j-M-Y ');
$expire_date = "02:59PM 26-Mar-2019";
if($now > $expire_date)
{
echo " link is expired";
}
else{
echo " link still alive ";
}
I guess I am missing something in the above code, somehow the above code isn't working if anyone would point out the right direction or some better implementation it would be great.
You are comparing the times as strings. This does not work, as your first formatted string has a leading space.
Instead, try either removing the whitespace, or better, compare the times as DateTime objects:
$timezone = new \DateTimeZone('Asia/kolkata');
// Create the current DateTime object
$currentDateTime = new \DateTime();
$currentDateTime-> setTimezone($timezone);
// Create the given DateTime object
$expire_date = "02:59PM 26-Mar-2019";
$expireDateTime = \DateTime::createFromFormat($expire_date, 'h:iA j-M-Y');
// Compare the objects
if($currentDateTime > $expireDateTime)
{
echo " link is expired";
}
else{
echo " link still alive ";
}
If you want to compare dates in PHP, your best bet is to use UNIX time stamps. A UNIX time stamp is the number of seconds since the UNIX epoch (00:00:00 Thursday, 1 January 1970).
time() will return the current UNIX time stamp.
strtotime() will convert a date string into a UNIX time stamp.
So replacing these two lines:
$now = $currentDateTime-> format(' h:iA j-M-Y ');
$expire_date = "02:59PM 26-Mar-2019";
With these:
$now = time();
$expire_date = strtotime("02:59PM 26-Mar-2019");
Should solve your problem.
You are comparing date strings which will not work. You have to parse the string to a datetime object or timestamp before you can compare these values.
For example, using timestamps:
$expire_date = "02:59PM 26-Mar-2019";
if (time() > strtotime($expire_date)) {
echo "link is expired";
} else {
echo "link still alive ";
}
All you have to do is use strtotime function and add inside date function and here you can specify day, hour, minutes, seconds as a perimeter. This way you can set time manually by adding +5 minutes or so on..
date_default_timezone_set("Asia/Kolkata"); // set time_zone according to your location
$created = "2020-08-14 17:52"; // time when link is created
$expire_date = date('Y-m-d H:i',strtotime('+1 minutes',strtotime($created)));
//+1 day = adds 1 day
//+1 hour = adds 1 hour
//+10 minutes = adds 10 minutes
//+10 seconds = adds 10 seconds
//To sub-tract time its the same except a - is used instead of a +
$now = date("Y-m-d H:i:s"); //current time
if ($now>$expire_date) { //if current time is greater then created time
echo " Your link is expired";
}
else //still has a time
{
echo " link is still alive";
}
In the application that I'm working on, the user must choose a date/time which is at least 5 minutes into the future. For this, I'm trying to implement a check. Below is the code which checks the time difference between the current time and chosen time.
$cur_date = new DateTime();
$cur_date = $cur_date->modify("+1 hours"); //fix the time since its an hour behind
$cur_date = $cur_date->format('m/d/Y g:i A');
$to_time = strtotime($chosen_date);
$from_time = strtotime($cur_date);
echo round(abs($from_time - $to_time) / 60,2). " minute"; //check the time difference
This tells me the time difference from the chosen time and the current time in minutes. So let's say the current time is 09/22/2015 5:53 PM and the chosen time is 09/22/2015 5:41 PM - it will tell me the difference which is 12 minutes.
What I want to know is how I can tell if those 12 minutes are into the future or in the past. I want my application to only proceed if the chosen time is at least 5 minutes into the future.
You're doing too much work. Just use DateTime() to do the date math for you:
// Wrong way to do this. Work with timezones instead
$cur_date = (new DateTime()->modify("+1 hours"));
// Assuming acceptable format for $chosen_date
$to_time = new DateTime($chosen_date);
$diff = $cur_date->diff($to_time);
if ($diff->format('%R') === '-') {
// in the past
}
echo $diff->format('%i') . ' minutes';
Demo
$enteredDate = new DateTime($chosen_date)->getTimestamp();
$now = new DateTime()->getTimestamp();
if(($enteredDate-$now)/60 >=5)echo 'ok';
Basically, the code takes the two dates converted in seconds since 1/1/1970. We calculate the difference between the two dates and divide the result by 60 as we want minutes. If there is a difference of at least 5 minutes, we're ok. If the number is negative, then we are in the past.
If anyone is looking to do something similar, I found the Carbon library which is included by default with the framework I am using (Laravel 5), it was much easier to do this calculation.
$chosen_date = new Carbon($chosen_date, 'Europe/London');
$whitelist_date = Carbon::now('Europe/London');
$whitelist_date->addMinutes(10);
echo "Chosen date must be after this date: ".$whitelist_date ."</br>";
echo "Chosen Date: ".$chosen_date ."</br>";
if ($chosen_date->gt($whitelist_date)) {
echo "proceed";
} else {
echo "dont proceed";
}
Is there a way to convert an input time string (ex: 01:13) to a Zend date object, so that I store it later in a timestamp column in a Mysql database.
Examples:
If the current datetime is 2013-07-15 17:33:07 and the user inputs 18:05 the output should be 2013-07-15 18:05:00.
If the current datetime is 2013-07-15 17:33:07 and the user inputs 02:09 the output should be 2013-07-16 02:09:00. Notice that since the time entered was lower than the current time, so it was treated as tomorrows time.
I simply want to get the next point in time that satisfies the entered time. I'm open for solution using plain PHP or Zend_Date.
I think you should compare the current time with the time entered by the user and create a DateTime object of either "today" or "tomorrow". DateTime accepts strtotime() relative time parameters.
Quick hack. Works as of today, 15.07.2013 23:58 local time:
$nextTime = new DateTime('today 18:10');
if ($nextTime < new DateTime('now')) { // DateTime comparison works since 5.2.2
$nextTime = new DateTime('tomorrow 18:10');
}
echo $nextTime->format('d.m.Y H:i:s');
here is working example for you just add your dynamic variable to check date with user inputs
You can use mktime function to manage your date.
$input_date = date("Y-m-d H:i:s",mktime(18,05,0,date("m"),date("d"),date("Y")));
echo "current time".$current_time = date('Y-m-d H:m:s');
echo "<br>User input is ".$input_date;
if(strtotime($current_time) > strtotime($input_date)){
$input_date = date("Y-m-d H:i:s",mktime(18,05,0,date("m"),date("d")+1,date("Y")));
echo "in";
}else{
// nothing to do
}
echo "<br> result->".$input_date;
i hope it will sure solve your issue