Calculate months between two dates using DateInterval without wrapping within a year - php

I am aware this topic is pretty exhausted, but obviously not quite enough!
$temp_d1 = new DateTime(date('Y-m-d', $fromTime)); // 2012-01-01
$temp_d2 = new DateTime(date('Y-m-d', $endTime)); // 2013-02-01
$interval = $temp_d2->diff($temp_d1);
$monthsAhead = $interval->format('%m'); // returns 1, but I am expecting 13
How do you calculate the number of months between two dates without wrapping within a 12 month scale?

I was confusing exactly what:
$monthsAhead = $interval->format('%m');
does.
Obviously, format('%m') is just formatting the month component of the DateInterval object, not necessarily 'give me the interval as a number of months'.
In my case, I was looking for/to do this:
$monthsAhead = $interval->m + ($interval->y * 12);
http://www.php.net/manual/en/class.dateinterval.php
Hope this helps other fools in the future!

Related

Decreasing DateInterval not producing result

Trying to get the last four Sundays, decrementing in a loop starting with the most recent Sunday first.
// most recent sunday
$mostRecentSunday = new DateTime('last sunday');
// four Sundays ago
$maxDateAttempt = clone $mostRecentSunday;
$maxDateAttempt->modify('-4 weeks');
// interval of one week (same as 7 days or "P7D")
$dateInterval = new DateInterval('P1W');
// isn't this supposedly supposed to switch the increasing interval to decreasing?
$dateInterval->invert = 1;
$dateRange = new DatePeriod($mostRecentSunday, $dateInterval, $maxDateAttempt);
foreach ($dateRange as $day) {
echo $day->format('F j, Y');
}
Taking #hijarian's answer in this similar question, I thought setting the invert property would solve this, but I cannot get it to work. Then this comment in the PHP docs claims the DatePeriod class isn't even compatible with negative intervals. Anyone have some clarity in the issue? Maybe the PHP docs could use some improvement here.
That comment in the PHP docs is only partially correct. Everything I've read and experimented with so far seems to indicate that DatePeriod doesn't work with negative DateIntervals when using an end date. Maybe there's some initial check that the minimum is less than the maximum before it does anything, but I'm really not sure why it doesn't work.
However, it does work if you use the recurrences constructor rather than setting an end date.
$dateRange = new DatePeriod($mostRecentSunday, $dateInterval, 3);
// using 3 rather than 4 because the initial value is one occurrence
But you have to create your DateInterval like this instead:
$dateInterval = DateInterval::createFromDateString('-1 week');
Interestingly enough, that does not create a 7 day interval with invert=1. If you var_dump($dateInterval), you'll see public 'd' => int -7 and public 'invert' => int 0.
But technically you don't need DateInterval or DatePeriod to accomplish this.
for ($i=0, $date = new DateTime; $i < 4; $i++) {
echo $date->modify('last sunday')->format('F j, Y');
}

Why the difference between two DateTime objects does not work?

I've got a problem with my "DateTime difference code":
$timeStart = new DateTime('2015-11-28');
$timeEnd = new DateTime('2016-11-28');
$interval = $timeEnd->diff($timeStart);
$result = $interval->format('%d');
echo $result." day(s)";
When I visualize $result, PHP show me 0. But between those two dates there are more days than 0 day...
php does not calculate the difference between two dates that are not in the same year?
Because there are 0 days difference. There is however a 1 year difference. If you changed %d to %y you'd get 1. So there's a difference of 1 year, 0 months and 0 days.
What you can use instead is the days property on DateInterval, as such:
$result = $interval->days;
Okay, I'm aware the answer was given already. But below is just a bit explanation.
In fact, DateInterval::format() does makes sense when you have a fixed amount of time (in years, months, days, hours), like this:
$interval = new DateInterval('P2Y4DT6H8M');
echo $interval->format('%d days');
That isn't your case!
where you have a relative time (2016-11-28 related to 2015-11-28) at all. In this specific case you want the days amount past since 28-11-2015.
That's why DateInterval::days (DateTime::diff() returns a DateInterval object) makes sense:
$start = new DateTime('2015-11-28');
$end = new DateTime('2016-12-28');
var_dump($end->diff($start)->days);

PHP adding exact weekdays to a timestamp

I want to add an x number of week days (e.g. 48 weekday hours) to the current timestamp. I am trying to do this using the following
echo (strtotime('2 weekdays');
However, this doesn't seem to take me an exact 48 hours ahead in time. For example, inputting the current server time of Tuesday 18/03/2014 10:47 returns Thursday 20/03/2014 00:00. using the following function:
echo (strtotime('2 weekdays')-mktime())/86400;
It can tell that it's returning only 1.3 weekdays from now.
Why is it doing this? Are there any existing functions which allow an exact amount of weekday hours?
Given you want to preserve the weekdays functionality and not loose the hours, minutes and seconds, you could do this:
$now = new DateTime();
$hms = new DateInterval(
'PT'.$now->format('H').'H'.
$now->format('i').'M'.
$now->format('s').'S'.
);
$date = new DateTime('2 weekdays');
$date->add($hms);//add hours here again
The reason why weekday doesn't add the hours is because, if you add 1 weekday at any point in time on a monday, the next weekday has to be tuesday.
The hour simply does not matter. Say your date is 2014-01-02 12:12:12, and you want the next weekday, that day starts at 2014-01-03 00:00:00, so that's what you get.
My last solution works though, and here's how: I use the $now instance of DateTime, and its format method to construct a DateInterval format string, to be passed to the constructor. An interval format is quite easy: it starts with P, for period, then a digit and a char to indicate what that digit represents: 1Y for 1 Year, and 2D for 2 Days.
However, we're only interested in hours, minutes and seconds. Actual time, which is indicated using a T in the interval format string, hence we start the string with PT (Period Time).
Using the format specifiers H, i and s, we construct an interval format that in the case of 12:12:12 looks like this:
$hms = new DateInterval(
'PT12H12M12S'
);
Then, it's a simple matter of calling the DateTime::add method to add the hours, minutes and seconds to our date + weekdays:
$weekdays = new DateTime('6 weekdays');
$weekdays->add($hms);
echo $weekdays->format('Y-m-d H:i:s'), PHP_EOL;
And you're there.
Alternatively, you could just use the basic same trick to compute the actual day-difference between your initial date, and that date + x weekdays, and then add that diff to your initial date. It's the same basic principle, but instead of having to create a format like PTXHXMXS, a simple PXD will do.
Working example here
I'd urge you to use the DateInterface classes, as it is more flexible, allows for type-hinting to be used and makes dealing with dates just a whole lot easier for all of us. Besides, it's not too different from your current code:
$today = new DateTime;
$tomorrow = new DateTime('tomorrow');
$dayAfter = new DateTime('2 days');
In fact, it's a lot easier if you want to do frequent date manipulations on a single date:
$date = new DateTime();//or DateTime::createFromFormat('Y-m-d H:i:s', $dateString);
$diff = new DateInterval('P2D');//2 days
$date->add($diff);
echo $date->format('Y-m-d H:i:s'), PHP_EOL, 'is the date + 2 days', PHP_EOL;
$date->sub($diff);
echo $date->format('Y-m-d H:i:s'), PHP_EOL, 'was the original date, now restored';
Easy, once you've spent some time browsing through the docs
I think I have found a solution. It's primitive but after some quick testing it seems to work.
The function calculates the time passed since midnight of the current day, and adds it onto the date returned by strtotime. Since this could fall into a weekend day, I've checked and added an extra day or two accordingly.
function weekDays($days) {
$tstamp = (strtotime($days.' weekdays') + (time() - strtotime("today")));
if(date('D',$tstamp) == 'Sat') {
$tstamp = $tstamp + 86400*2;
}
elseif(date('D',$tstamp) == 'Sun') {
$tstamp = $tstamp + 86400;
}
return $tstamp;
}
Function strtotime('2 weekdays') seems to add 2 weekdays to the current date without the time.
If you want to add 48 hours why not adding 2*24*60*60 to mktime()?
echo(date('Y-m-d', mktime()+2*24*60*60));
The currently accepted solution works, but it will fail when you want to add weekdays to a timestamp that is not now. Here's a simpler snippet that will work for any given point in time:
$start = new DateTime('2021-09-29 15:12:10');
$start->add(date_interval_create_from_date_string('+ 3 weekdays'));
echo $start->format('Y-m-d H:i:s'); // 2021-10-04 15:12:10
Note that this will also work for a negative amount of weekdays:
$start = new DateTime('2021-09-29 15:12:10');
$start->add(date_interval_create_from_date_string('- 3 weekdays'));
echo $start->format('Y-m-d H:i:s'); // 2021-09-24 15:12:10

Adding two DateTime objects in php [duplicate]

This question already has answers here:
How we can add two date intervals in PHP
(4 answers)
Closed 9 years ago.
I have two DateTime object.
$time1 = new DateTime('01:04:00');
$time2 = new DateTime('00:13:22');
Addition of this two will be : 01:17:22. How can I do it?
A "time of day" is not the same thing as a "duration of time". It doesn't makes sense to add together two time of day values - regardless of platform or language. Think about it - what does "11:00 PM" + "4:00 AM" equal? It's a nonsensical question.
You should be thinking about PHP's DateInterval class, not the DateTime class.
It should be noted that if you follow the examples on the dup posts of using strtotime it will work only when each individual input, and the final result, are all under 24 hours. Why? Because that's the maximum amount of time allowed in a standard day. That's the consequence of mixing "time of day" with "duration of time".
This should work for you:
function time_to_interval($time) {
$parts = explode(':',$time);
return new DateInterval('PT'.$parts[0].'H'.$parts[1].'M'.$parts[2].'S');
}
function add_intervals($a,$b) {
$zerodate = new DateTime('0000-01-01 00:00:00');
$dt = clone $zerodate;
$dt->add($a);
$dt->add($b);
return $zerodate->diff($dt);
}
function format_interval_hhmmss($interval){
$totalhours = $interval->h + ($interval->d * 24);
return $totalhours.$interval->format(':%I:%S');
}
$interval1 = time_to_interval('01:04:00');
$interval2 = time_to_interval('00:13:22');
$interval3 = add_intervals($interval1,$interval2);
echo format_interval_hhmmss($interval3);
Note that the choice of value for $zerodate isn't really all that important. It's just that some reference point is required, since PHP doesn't provide operations directly on DateInterval.
Also note that the the DateInterval::format function doesn't have a formatter to get you total number of hours inclusive of days, so if there's any chance the total could be 24 hours or more, then you have to format that part yourself, like I showed in the format_interval_hhmmss function.
Also note that my PHP skills are not all that great, so there may be a more efficient way to write these functions.
function addtime($time1,$time2)
{
$x = new DateTime($time1);
$y = new DateTime($time2);
$interval1 = $x->diff(new DateTime('00:00:00')) ;
$interval2 = $y->diff(new DateTime('00:00:00')) ;
$e = new DateTime('00:00');
$f = clone $e;
$e->add($interval1);
$e->add($interval2);
$total = $f->diff($e)->format("%H:%I:%S");
return $total;
}
The only built-in DateTime add/substract methods require using a DateInterval. e.g
$t1 = new DateTime('01:04:33');
$new = $t1->add(new DateInterval('PT13M22S'));
^^^^^^---13 minutes, 22 seconds
however, note that since DateTime works on DATES as well as times, you can't just slam together two times like this and expect to get reliable results. Consider what happens if you're doing the addition on an interval that happens to span a daylight savings border, or crosses over a day boundary, etc...

PHP date calculation

What is the best (date format independent way) in PHP to calculate difference in days between two dates in specified format.
I tried the following function:
function get_date_offset($start_date, $end_date)
{
$start_time = strtotime($start_date);
$end_time = strtotime($end_date);
return round(($end_time-$start_time)/(3600*24));
}
It works ok on linux box, but when running under windows strtotime returns ''.
EDIT:
Input date is in mm/dd/yyyy format, but I would like to make it accept $format as a parameter.
I need only difference in days.
If you do not want or you cannot use Zend Framework or Pear package try this function i hope this would help:
function dateDifference($date1, $date2)
{
$d1 = (is_string($date1) ? strtotime($date1) : $date1);
$d2 = (is_string($date2) ? strtotime($date2) : $date2);
$diff_secs = abs($d1 - $d2);
$base_year = min(date("Y", $d1), date("Y", $d2));
$diff = mktime(0, 0, $diff_secs, 1, 1, $base_year);
return array
(
"years" => abs(substr(date('Ymd', $d1) - date('Ymd', $d2), 0, -4)),
"months_total" => (date("Y", $diff) - $base_year) * 12 + date("n", $diff) - 1,
"months" => date("n", $diff) - 1,
"days_total" => floor($diff_secs / (3600 * 24)),
"days" => date("j", $diff) - 1,
"hours_total" => floor($diff_secs / 3600),
"hours" => date("G", $diff),
"minutes_total" => floor($diff_secs / 60),
"minutes" => (int) date("i", $diff),
"seconds_total" => $diff_secs,
"seconds" => (int) date("s", $diff)
);
}
The PEAR Date class offers all kinds of features for finding the differences between dates and about 1000 other things as well. The docs for it are here...
The problem with PHP is that it doesn't have a definite DateTime type. You can use a Unix timestamp, or the built-in DateTime class, but they are pretty limited in their functionality. I expect that there should be some 3rd party classes with more extensive support for date-time calculations, but I haven't looked for it.
Using Unix timestamps for date (not time) calculations is also tricky. You'd have to discard the time part, but simply resetting to 00:00 is not safe because of daylight savings time (DST). DST has the effect that there are two days every year that don't have exactly 24 hours. Thus, when adding/subtracting dates you might end up with a value that does not divide evenly with 3600*24.
I'd suggest looking for some 3rd party class that has proper support for all this stuff. Date/Time calculations are awesome in their ugliness. :P
The Zend Framework has the class Zend_Date for dealing with "date math". It works around system specific timestamp limits by using the BCMath extension, or if that's not available limits the timestamps by max float value for your system.
// example printing difference in days
require('Zend/Date.php');
$date1 = new Zend_Date();
$date1->set(2, Zend_Date::MONTH);
$date1->set(27, Zend_Date::DAY);
$date1->set(2008, Zend_Date::YEAR);
$date2 = new Zend_Date();
$date2->set(3, Zend_Date::MONTH);
$date2->set(3, Zend_Date::DAY);
$date2->set(2008, Zend_Date::YEAR);
echo ($date2->getTimestamp() - $date1->getTimestamp()) / (3600*24);
I'm not sure what is considered best, since there is no built-in function in PHP for doing this, but some people have used gregoriantojd(), for example in this forum post.
gregoriantojd() gives the same results as using strtotime(), see this blogpost for how to do it:
http://www.phpro.org/examples/Calculate-Age-With-PHP.html
The following works for me. Believe I found it on the php.net docs somewhere.
*Edit - Woops, didn't see csl's post. This is the exact function from his link, must have been where I found it. ;)
//Find the difference between two dates
function dateDiff($startDate, $endDate)
{
// Parse dates for conversion
$startArry = date_parse($startDate);
$endArry = date_parse($endDate);
// Convert dates to Julian Days
$start_date = gregoriantojd($startArry["month"], $startArry["day"], $startArry["year"]);
$end_date = gregoriantojd($endArry["month"], $endArry["day"], $endArry["year"]);
// Return difference
return round(($end_date - $start_date), 0);
}
I was trying to calculate the difference of two dates for the purpose of showing the duration of an event. Most of the functions given on the problem fails if the event has a duration form friday at 17:00 to sunday at 15:00. My goal was to find the difference between the dates like:
date('Ymd',$end)-date('Tmd',$begining);
But that is likly to fail because there isn't 99 month in a year and 99 days in a month. I could convert the date string to UNIX timestamp and divide by 60*60*12, but some days have a greater or lesser number of hours, sometimes there's eaven a leap secound. So I made my own function using getdate() a function that returns an array of innformation about the timestamp.
/*
* datediff($first,$last)
* $first - unix timestamp or string aksepted by strtotime()
* $last - unix timestamp or string aksepted by strtotime()
*
* return - the difference in days betveen the two dates
*/
function datediff($first,$last){
$first = is_numeric($first) ? $first : strtotime($first);
$last = is_numeric($last ) ? $last : strtotime($last );
if ($last<$first){
// Can't calculate negative difference in dates
return -1;
}
$first = getdate($first);
$last = getdate($last );
// find the difference in days since the start of the year
$datediff = $last['yday'] - $first['yday'];
// if the years do not match add the appropriate number of days.
$yearCountedFrom = $first['year'];
while($last['year'] != $yearCountedFrom ){
// Take leap years into account
if( $yearCountedFrom % 4 == 0 && $yearCountedFrom != 1900 && $yearCountedFrom != 2100 ){
//leap year
$datediff += 366;
}else{
$datediff += 365;
}
$yearCountedFrom++;
}
return $datediff;
}
Concerning the GregorianToJD() function, it might work, but I feel a little bit uneasy since I do not understand how it work.
Let's not overengineer, guys.
$d1 = strtotime($first_date);
$d2 = strtotime($second_date);
$delta = $d2 - $d1;
$num_days = ($delta / 86400);
Calculate the difference between two Dates (and time) using Php. The following page provides a range of different methods (7 in total) for performing date / time calculations using Php, to determine the difference in time (hours, munites), days, months or years between two dates.
See Php Date Time - 7 Methods to Calculate the Difference between 2 dates
PHP 5.2 introduces the DateTime and DateInterval classes which makes this easy:
function get_date_offset($start_date, $end_date)
{
$start_time = new DateTime($start_date);
$end_time = new DateTime($end_date);
$interval = $end_time->diff($start_time);
return $interval->format('%a days');
}

Categories