What's the best way of adding a time interval in PHP? - php

I want to add time to an existing date. I have 2 string variables:
$date = "2013-01-05 10:55:15";
$interval = "50:25:10";
I want to calculate the final date "2013-01-07 13:20:25". The hours in time can be bigger than 23, meaning that this interval can be greater than a whole day.
What's the best way to do this ?

Use DateTime API:
$date = new DateTime("2013-01-05 10:55:15");
$date->add(new DateInterval("PT50H25M10S"));
then you can convert it back to string with the same date format you would use with date() function, if you want to:
$string = $date->format("Y-m-d H:i:s");
For more information about the DateInterval definition, visit this page:
DateInterval
The format starts with the letter P, for "period." Each duration
period is represented by an integer value followed by a period
designator. If the duration contains time elements, that portion of
the specification is preceded by the letter T.
Here are some simple examples. Two days is P2D. Two seconds is PT2S.
Six years and five minutes is P6YT5M.
so in this case PT50H25M10S means 50 hours, 25 minutes, and 10 seconds
Note that DateInterval is available only since PHP 5.3, if you have to use lower version, you could use something like this:
$time = strtotime("2013-01-05 10:55:15");
$time += 55*60*60 + 25*60 + 10;
$newDate = date("Y-m-d H:i:s");

This is a little tricky.
Normally what you would do here if it was a static period, or was a single period type, is something along the lines of:
$date = "2013-01-05 10:55:15";
$time = new DateTime($date);
$time->add(new DateInterval('PT5M'));
This would add 5 minutes to the datetime. However I doubt you can pass the whole interval in. So what you'll probably have to do is split the interval by : and then add each part of the interval (I assume it is dynamic?) to the date separately. So first hours, then minutes, then seconds
For more on this, see here: http://www.php.net/manual/en/datetime.add.php

You could first explode the interval and then get the hours, minutes, seconds, and then use DateTime's add() to add the interval, like so:
$interval = '50:25:10';
$datestring = '2013-01-05 10:55:15';
list($hours, $minutes, $seconds) = explode(':', $interval);
$date = new DateTime($datestring);
$date->add(new DateInterval('PT'.$hours.'H'.$minutes.'M'.$seconds.'S'));
echo $date->format('Y-m-d H:i:s');
Demo!

Use a DateInterval and DateTime->add():
$date = new DateTime("2013-01-05 10:55:15");
$date->add(new DateInterval("PT50H25M10S"));

In case anyone needs based on answers above, I made my function like this:
public function addTime($date, $time) {
$atime = explode(":", $time);
$_date = date_create_from_format("Y-m-d H:i:s", $date);
$_date->add(new DateInterval("PT" . intval($atime[0])."H".intval($atime[1])."M".intval($atime[2])."S"));
return $_date->format("Y-m-d H:i:s");
}
hope it helps someone

Related

How to get next time occurrence from now, submitted in php?

I have an input file with type time. what I want to do is to get time from the moment now that is if time right now is 2019-11-26 23:50:00 and the value of input field if 22:30:00, I should get a date 2019-11-27 22:30:00.
How can achieve this in PHP? in short, get the datetime stamp for the next occurrence of 22:30:00 which is 2019-11-27 22:30:00 as per the given example.
answer found but can we optimize the code more ?
$a = explode(':', date('H:i:s', strtotime($validateData['time'])));
$str = '+'.$a[0].' hours '.$a[1].' minutes '.$a[2].' seconds';
$trigger_at = date(date('Y-m-d H:i:s', strtotime( $str, strtotime($validateData['date']))));
return $trigger_at;
This is simpler and a lot more readable
$time = "21:30:00"; // Time from input
$today = date("Y-m-d $time");
$tomorrow = date("Y-m-d H:i:s", strtotime($today)+86400);
$date = strtotime($today) < strtotime("now") ? $tomorrow : $today;
Explanation: We take timestamp at specified hour for today and tomorrow, if today timestamp has been passed, we use tomorrow timestamp. Simple. :)
All you are doing is appending (concatenating) a string onto another string.
$time = "22:30:00"; // This is the time you have
$date = date("Y-m-d"); // Right now in yyyy-mm-dd format.
$newdatetime = $date.' '.$time;
That will give you the current date with the supplied time appended to it. You can convert that back into a timestamp using:
$timestamp = strtotime($newdatetime);
The answer below is based on the original question in which the time was assumed to be an offset from now. It is left here simply to avoid deleting a lot of code.
The function strtotime is easy to use for that. However, it doesn't accept HH:MM:SS format. So, you have to alter the string. I would do it like:
$time = "22:30:00"; // This is the time you have
$a = explode(':', $time);
$str = '+'.$a[0].' hours '.$a[1].' minutes '.$a[2].' seconds'; // This breaks it into separate numbers with labels.
$date = date("Y-m-d h:i:s", strtotime($str)); // The adjusted date
You can change the format of the output as you like by changing the first string used in the date function.

Check with Carbon if a date is on a different day [duplicate]

I have two Datetimes like this (the dates being actually $vars)
$startTime = \DateTime::createFromFormat('Y/m/d H:i', '2015/01/01 23:00');
$endTime = \DateTime::createFromFormat('Y/m/d H:i', '2015/01/02 01:00');
I struggle with a (possibly pretty) simple problem: How could I determine if the two dates are on different calendar days?
I cannot do < as 2015/01/01 22:00 < 2015/01/01 23:00 would also be true. I can also not do this:
$diff = $startTime->diff($endTime);
$days = $diff->format('%d');
echo $days;
as it gives me 0.
THIS gives me an idea about how to do it, but for javascript, what would be the equivalent for php?
//UPDATE
$startDate = $startTime->format('Y/m/d');
$endDate = $endTime->format('Y/m/d');
$diffDates = $startDate->diff($endDate);
$daysDiff = $diffDates->format('%d');
echo $daysDiff;
I think that might be the right approach now, thanks to the comments, but now I get Error: Call to a member function diff() on string
//UPDATE FOR CLARIFICATION WHAT I'M TRYING TO DO
I just want to have the difference in days, so for the above it would be '1' (although only 2 hours difference actually) and for example '2015/01/01 23:00' and '2015/01/03 17:00' would be '2'.
Just create the dates with time set to 00:00:00:
$startTime = \DateTime::createFromFormat('Y/m/d H:i:s', '2015/01/01 00:00:00');
$endTime = \DateTime::createFromFormat('Y/m/d H:i:s', '2015/01/02 00:00:00');
or reset time to zero on existing dates:
$startTime->setTime(0, 0, 0);
$endTime->setTime(0, 0, 0);
then it should work:
$diff = $startTime->diff($endTime);
$days = $diff->format('%d');
echo $days; // 1
Bonus
If you want to work only with dates, remember to set the time to 00:00:00 in createFromFormat or reset it with setTime. If you won't provide time in createFromFormat PHP will set it to the current time:
$date = DateTime::createFromFormat('Y-m-d', '2016-01-21');
print $date->format('H:i:s'); //not 00:00:00
To fix it, you must either:
provide 00:00:00 time in format:
$date = DateTime::createFromFormat('Y-m-d H:i:s', '2016-01-21 00:00:00');
prefix the date format with exclamation mark and omit the time, this will set the time to 00:00:00 automatically:
$date = DateTime::createFromFormat('!Y-m-d', '2016-01-21');
reset the time after creation:
$date = DateTime::createFromFormat('Y-m-d', '2016-01-21');
$date->setTime(0, 0);
I think this is one of the few situations where the use of strings for date calculations is justified:
function onDifferentDays(\DateTimeInterface $startTime, \DateTimeInterface $endTime){
return $startTime->format('Y-m-d')!==$endTime->format('Y-m-d');
}
This code should be easy to extend to include time zone.
There're other alternatives but I don't think they're normally worth the effort:
Compare element by element (day, month and year):
The PHP DateTime class doesn't offer dedicated functions, only format().
Normalize both dates to a common time and compare with == (not ===):
Unless you're using immutable objects you need to clone input or expect side effects
You also need to ensure that time exists in the active time zone though midnight is probably safe enough.
Whatever, YMMV ;-)
Comparing formatted dates is the right thing to do:
$a->format('Y-m-d') === $b->format('Y-m-d')
There is a method for that if you use Carbon:
$dt1->isSameDay($dt2)
So I recommend to use it instead of previous answers given here.
http://carbondoc/docs/#api-comparison

PHP check if two Datetimes are not on the same calendar day

I have two Datetimes like this (the dates being actually $vars)
$startTime = \DateTime::createFromFormat('Y/m/d H:i', '2015/01/01 23:00');
$endTime = \DateTime::createFromFormat('Y/m/d H:i', '2015/01/02 01:00');
I struggle with a (possibly pretty) simple problem: How could I determine if the two dates are on different calendar days?
I cannot do < as 2015/01/01 22:00 < 2015/01/01 23:00 would also be true. I can also not do this:
$diff = $startTime->diff($endTime);
$days = $diff->format('%d');
echo $days;
as it gives me 0.
THIS gives me an idea about how to do it, but for javascript, what would be the equivalent for php?
//UPDATE
$startDate = $startTime->format('Y/m/d');
$endDate = $endTime->format('Y/m/d');
$diffDates = $startDate->diff($endDate);
$daysDiff = $diffDates->format('%d');
echo $daysDiff;
I think that might be the right approach now, thanks to the comments, but now I get Error: Call to a member function diff() on string
//UPDATE FOR CLARIFICATION WHAT I'M TRYING TO DO
I just want to have the difference in days, so for the above it would be '1' (although only 2 hours difference actually) and for example '2015/01/01 23:00' and '2015/01/03 17:00' would be '2'.
Just create the dates with time set to 00:00:00:
$startTime = \DateTime::createFromFormat('Y/m/d H:i:s', '2015/01/01 00:00:00');
$endTime = \DateTime::createFromFormat('Y/m/d H:i:s', '2015/01/02 00:00:00');
or reset time to zero on existing dates:
$startTime->setTime(0, 0, 0);
$endTime->setTime(0, 0, 0);
then it should work:
$diff = $startTime->diff($endTime);
$days = $diff->format('%d');
echo $days; // 1
Bonus
If you want to work only with dates, remember to set the time to 00:00:00 in createFromFormat or reset it with setTime. If you won't provide time in createFromFormat PHP will set it to the current time:
$date = DateTime::createFromFormat('Y-m-d', '2016-01-21');
print $date->format('H:i:s'); //not 00:00:00
To fix it, you must either:
provide 00:00:00 time in format:
$date = DateTime::createFromFormat('Y-m-d H:i:s', '2016-01-21 00:00:00');
prefix the date format with exclamation mark and omit the time, this will set the time to 00:00:00 automatically:
$date = DateTime::createFromFormat('!Y-m-d', '2016-01-21');
reset the time after creation:
$date = DateTime::createFromFormat('Y-m-d', '2016-01-21');
$date->setTime(0, 0);
I think this is one of the few situations where the use of strings for date calculations is justified:
function onDifferentDays(\DateTimeInterface $startTime, \DateTimeInterface $endTime){
return $startTime->format('Y-m-d')!==$endTime->format('Y-m-d');
}
This code should be easy to extend to include time zone.
There're other alternatives but I don't think they're normally worth the effort:
Compare element by element (day, month and year):
The PHP DateTime class doesn't offer dedicated functions, only format().
Normalize both dates to a common time and compare with == (not ===):
Unless you're using immutable objects you need to clone input or expect side effects
You also need to ensure that time exists in the active time zone though midnight is probably safe enough.
Whatever, YMMV ;-)
Comparing formatted dates is the right thing to do:
$a->format('Y-m-d') === $b->format('Y-m-d')
There is a method for that if you use Carbon:
$dt1->isSameDay($dt2)
So I recommend to use it instead of previous answers given here.
http://carbondoc/docs/#api-comparison

Given a Unix timestamp, how to get beginning and end of that day?

I have a Unix timestamp like this:
$timestamp=1330581600
How do I get the beginning of the day and the end of the day for that timestamp?
e.g.
$beginOfDay = Start of Timestamp's Day
$endOfDay = End of Timestamp's Day
I tried this:
$endOfDay = $timestamp + (60 * 60 * 23);
But I don't think it'll work because the timestamp itself isn't the exact beginning of the day.
strtotime can be used to to quickly chop off the hour/minutes/seconds
$beginOfDay = strtotime("today", $timestamp);
$endOfDay = strtotime("tomorrow", $beginOfDay) - 1;
DateTime can also be used, though requires a few extra steps to get from a long timestamp
$dtNow = new DateTime();
// Set a non-default timezone if needed
$dtNow->setTimezone(new DateTimeZone('Pacific/Chatham'));
$dtNow->setTimestamp($timestamp);
$beginOfDay = clone $dtNow;
$beginOfDay->modify('today');
$endOfDay = clone $beginOfDay;
$endOfDay->modify('tomorrow');
// adjust from the start of next day to the end of the day,
// per original question
// Decremented the second as a long timestamp rather than the
// DateTime object, due to oddities around modifying
// into skipped hours of day-lights-saving.
$endOfDateTimestamp = $endOfDay->getTimestamp();
$endOfDay->setTimestamp($endOfDateTimestamp - 1);
var_dump(
array(
'time ' => $dtNow->format('Y-m-d H:i:s e'),
'start' => $beginOfDay->format('Y-m-d H:i:s e'),
'end ' => $endOfDay->format('Y-m-d H:i:s e'),
)
);
With the addition of extended time in PHP7, there is potential to miss a second if using $now <= $end checking with this.
Using $now < $nextStart checking would avoid that gap, in addition to the oddities around subtracting seconds and daylight savings in PHP's time handling.
Just DateTime
$beginOfDay = DateTime::createFromFormat('Y-m-d H:i:s', (new DateTime())->setTimestamp($timestamp)->format('Y-m-d 00:00:00'))->getTimestamp();
$endOfDay = DateTime::createFromFormat('Y-m-d H:i:s', (new DateTime())->setTimestamp($timestamp)->format('Y-m-d 23:59:59'))->getTimestamp();
First a DateTime object is created and the timestamp is set to the desired timestamp. Then the object is formatted as a string setting the hour/minute/second to the beginning or end of the day. Lastly, a new DateTime object is created from this string and the timestamp is retrieved.
Readable
$dateTimeObject = new DateTime();
$dateTimeObject->setTimestamp($timestamp);
$beginOfDayString = $dateTimeObject->format('Y-m-d 00:00:00');
$beginOfDayObject = DateTime::createFromFormat('Y-m-d H:i:s', $beginOfDayString);
$beginOfDay = $beginOfDayObject->getTimestamp();
We can get the end of the day in an alternate manner using this longer version:
$endOfDayObject = clone $beginOfDayOject(); // Cloning because add() and sub() modify the object
$endOfDayObject->add(new DateInterval('P1D'))->sub(new DateInterval('PT1S'));
$endOfDay = $endOfDayOject->getTimestamp();
Timezone
The timezone can be set as well by adding a timestamp indicator to the format such as O and specifying the timestamp after creating the DateTime object:
$beginOfDay = DateTime::createFromFormat('Y-m-d H:i:s O', (new DateTime())->setTimezone(new DateTimeZone('America/Los_Angeles'))->setTimestamp($timestamp)->format('Y-m-d 00:00:00 O'))->getTimestamp();
Flexibility of DateTime
We can also get other information such as the beginning/end of the month or the beginning/end of the hour by changing the second format specified. For month: 'Y-m-01 00:00:00' and 'Y-m-t 23:59:59'. For hour: 'Y-m-d H:00:00' and 'Y-m-d H:59:59'
Using various formats in combination with add()/sub() and DateInterval objects, we can get the beginning or end of any period, although some care will need to be taken to handle leap years correctly.
Relevant Links
From the PHP docs:
DateTime
date with info on the format
DateTimeZone
DateInterval
You can use a combination of date() and mktime():
list($y,$m,$d) = explode('-', date('Y-m-d', $ts));
$start = mktime(0,0,0,$m,$d,$y);
$end = mktime(0,0,0,$m,$d+1,$y);
mktime() is smart enough to wrap months/years when given a day outside the specified month (jan 32nd will be feb 1st, etc)
You could convert the time to the current data and then use the strtotime function to find the start of the day and simply add 24 hours to that to find the end of the day.
You could also use the remainder operator (%) to find the nearest day. For example:
$start_of_day = time() - 86400 + (time() % 86400);
$end_of_day = $start_of_day + 86400;
The accepted answer unfortunately breaks due to a php bug that occurs in very specific scenarios. I'll discuss those scenarios, but first the answer using DateTime. The only difference between this and the accepted answer occurs after the // IMPORTANT line:
$dtNow = new DateTime();
// Set a non-default timezone if needed
$dtNow->setTimezone(new DateTimeZone('America/Havana'));
$dtNow->setTimestamp($timestamp);
$beginOfDay = clone $dtNow;
// Go to midnight. ->modify('midnight') does not do this for some reason
$beginOfDay->modify('today');
// now get the beginning of the next day
$endOfDay = clone $beginOfDay;
$endOfDay->modify('tomorrow');
// IMPORTANT
// get the timestamp
$ts = $endOfDay->getTimestamp();
// subtract one from that timestamp
$tsEndOfDay = $ts - 1;
// we now have the timestamp at the end of the day. we can now use that timestamp
// to set our end of day DateTime
$endOfDay->setTimestamp($tsEndOfDay);
So you'll note that instead of using ->modify('1 second ago'); we instead get the timestamp and subtract one. The accepted answer using modify should work, but breaks because of php bug in very specific scenarios. This bug occurs in timezones that change daylight savings at midnight, on the day of the year that clocks are moved "forward". Here is an example you can use to verify that bug.
bug example code
// a time zone, Cuba, that changes their clocks forward exactly at midnight. on
// the day before they make that change. there are other time zones which do this
$timezone = 'America/Santiago';
$dateString = "2020-09-05";
echo 'the start of the day:<br>';
$dtStartOfDay = clone $dtToday;
$dtStartOfDay->modify('today');
echo $dtStartOfDay->format('Y-m-d H:i:s');
echo ', '.$dtStartOfDay->getTimestamp();
echo '<br><br>the start of the *next* day:<br>';
$dtEndOfDay = clone $dtToday;
$dtEndOfDay->modify('tomorrow');
echo $dtEndOfDay->format('Y-m-d H:i:s');
echo ', '.$dtEndOfDay->getTimestamp();
echo '<br><br>the end of the day, this is incorrect. notice that with ->modify("-1 second") the second does not decrement the timestamp by 1:<br>';
$dtEndOfDayMinusOne = clone $dtEndOfDay;
$dtEndOfDayMinusOne->modify('1 second ago');
echo $dtEndOfDayMinusOne->format('Y-m-d H:i:s');
echo ', '.$dtEndOfDayMinusOne->getTimestamp();
echo '<br><br>the end of the day, this is correct:<br>';
$dtx = clone $dtEndOfDay;
$tsx = $dtx->getTimestamp() - 1;
$dty = clone $dtEndOfDay;
$dty->setTimestamp($tsx);
echo $dty->format('Y-m-d H:i:s');
echo ', '.$tsx;
bug example code output
the start of the day:
2020-03-26 00:00:00, 1585173600
the start of the *next* day:
2020-03-27 01:00:00, 1585260000
the end of the day, this is incorrect. notice that with ->modify("1 second ago") the
second does not decrement the timestamp by 1:
2020-03-27 01:59:59, 1585263599
the end of the day, this is correct:
2020-03-26 23:59:59, 1585259999
Today Starting date timestamp. Simple
$stamp = mktime(0, 0, 0);
echo date('m-d-Y H:i:s',$stamp);
$start_of_day = floor (time() / 86400) * 86400;
$end_of_day = ceil (time() / 86400) * 86400;
If your need both values in the same script. It is faster to +/- 86400 seconds to one of the variables than to fire both floor and ceil. For example:
$start_of_day = floor (time() / 86400) * 86400;
$end_of_day = $start_of_day + 86400;
For anyone that have this question in the future:
Any day code
<?php
$date = "2015-04-12 09:20:00";
$midnight = strtotime("midnight", strtotime($date));
$now = strtotime($date);
$diff = $now - $midnight;
echo $diff;
?>
Current day code
<?php
$midnight = strtotime("midnight");
$now = date('U');
$diff = $now - $midnight;
echo $diff;
?>
$date = (new \DateTime())->setTimestamp(1330581600);
echo $date->modify('today')->format('Y-m-d H:i:s'); // 2012-02-29 00:00:00
echo PHP_EOL;
echo $date->modify('tomorrow - 1 second')->format('Y-m-d H:i:s'); // 2012-02-29 23:59:59
$startOfDay = new \DateTime('tomorrow');
$startOfDay->modify('-1 day');
This works for me :)
A little late to the party, but here's another easy way to achieve what you're looking for:
$timestamp=1330581600;
$format = DATE_ATOM;
$date = (new DateTime())->setTimestamp($timestamp);
// Here's your initial date, created from the timestamp above
// 2012-03-01T06:00:00+00:00
$dateFromTimestamp = $date->format($format);
// This is the beginning of the day
// 2012-03-01T00:00:00+00:00
$startOfDay = $date->setTime(0,0);
// This is the beginning of the next day
// 2012-03-02T00:00:00+00:00
$startOfNextDay = $startOfDay->modify('+1 day');
I would personally avoid using the end of the day unless it's absolutely necessary. You can, of course, use 23:59:59 but this is not the actual end of the day (there's still 1 second left). What I do is use the start of the next day as my end boundary, for example:
$start = new DateTime('2021-11-09 00:00:00');
$end = new DateTime('2021-11-10 00:00:00');
if ($someDateTime >= $start && $someDateTime < $end) {
// do something
}
If I must use the end of the day, I'd go with calculating the start of the next day and then subtracting 1 microsecond from that.
$beginOfDay = (new DateTime('today', new DateTimeZone('Asia/Tehran')))->getTimestamp();
$endOfDay = $beginOfDay + 86399;
You can set a timezone by replacing "Asia/Tehran". One day is 86400 seconds, Don't ask me why 86399, It is a whisper in my mind that says it is 86399, So I do not even want to think about its truth.

How do I remove 3 months from a date?

Assume the date is:
$date = "2011-08-28";
It need to calculate 3 months previous to $date - how can that be done?
$new_timestamp = strtotime('-3 months', strtotime($date));
You can then use the new timestamp with the php date() function to display the new date how you wish, for example:
echo date("Y-m-d",$new_timestamp);
For me this way is much better because the code is more readable.
$datetime = Datetime::createFromFormat('Y-m-d', "2011-08-28");
$datetime->modify('-3 months');
echo $datetime->format('Y-m-d');
edit: I'm an idiot. You could do the above as follows
$datetime = new Datetime("2011-08-28");
$datetime->modify('-3 months');
echo $datetime->format('Y-m-d');
edit: As pointed out by calumbrodie, you can use the sub method instead of inverting the interval and adding it to the date object
I was trying to do something similar to the original question. I needed to subtract 1 day from a DateTime object. I know the other solutions work, but here's another way I liked better. I used this function:
function getPreviousDay($dateObject){
$interval = new DateInterval('P1D');
$dateObject->sub($interval);
return $dateObject;
}
$dateObject is a DateTime object that can have any date you wan't, but as I wanted the current date, I wrote:
$dateObject = new DateTime('now');
What my function does, is subtract 1 day from the date it receives, but you can modify it so it subtracts 3 months modifying the DateInterval constructor like this:
$interval = new DateInterval('P3M');
$dateObject->sub($interval);
return $dateObject;
You can find the explanation on the string format used in the DateInterval constructor here
DateInterval constructor documentation
There you'll se that letter 'P' (period) is mandatory, the you use an int to specify period length and then specify the period type, in the case of months 'M'. It looks as if there was an error in the documentation, it says that "M" is used for months and minutes, but it actually works for months. If you need to use minutes, you must specify "PTM", for example "PT3M" for 3 minutes, outside the table it says you must use the "T" identifier for time intervals.
edit:
To give a complete example, you have to use this format for a full date time interval:
$format = 'P1Y2M4DT2H1M40S';
This will represent an interval of 1 year, 2 months, 4 days, 2 hours,1 minute and 40 seconds
Hope this helps someone
<?php
$threemonthsago = mktime(0, 0, 0, date("m")-3, date("d"), date("Y"));
?>
<?php
$date = date_create('2000-01-01');
date_add($date, date_interval_create_from_date_string('10 days'));
echo date_format($date, 'Y-m-d');
?>

Categories