Can somebody please explain me the result of DateTime::diff() in the following example:
$start = DateTime::createFromFormat('Y/m/d', '2013/05/11');
$end = DateTime::createFromFormat('Y/m/d', '2015/03/08');
$diff = $start->diff($end);
var_dump($diff);
exit;
Result:
object(DateInterval)#19 (15) {
["y"]=>
int(1)
["m"]=>
int(9)
["d"]=>
int(25)
["h"]=>
int(0)
["i"]=>
int(0)
["s"]=>
int(0)
["weekday"]=>
int(0)
["weekday_behavior"]=>
int(0)
["first_last_day_of"]=>
int(0)
["invert"]=>
int(0)
["days"]=>
int(666)
["special_type"]=>
int(0)
["special_amount"]=>
int(0)
["have_weekday_relative"]=>
int(0)
["have_special_relative"]=>
int(0)
}
This is what I think is happened here:
$diff->y is 1 because there is one full year between dates;
$diff->m is 9 because there are 9 full months between dates (without 2013/05 and 2015/03).
I'd like to know how number 25 is calculated. I can'f figure out where this number comes from.
I'd expect here the difference between 2013/05/11 and 2013/05/31 + 2015/03/01 and 2015/03/08, so 20 + 7 = 27. Instead I got 25.
This is what I think is happened here: $diff->y is 1 because there is one full year between dates; $diff->m is 9 because there are 9 full months between dates (without 2013/05 and 2015/03).
This assumption is correct.
I'd expect here the difference between 2013/05/11 and 2013/05/31 + 2015/03/01 + 2015/03/08, so 20 + 7 = 27.
This is rather confusingly written and it took me awhile to figure out exactly what you mean, but here's why you can't expect that ...
What difference would you expect there to be between 2013/05/11 (your original "from" date) and 2015/02/11? Exactly a year an 9 months, right?
Well, that's how it works - a full month difference is reached when the day of month matches. For the dates you've got, 2013/02/11 is the point where month difference count increases from 8 to 9, and the days start counting from that point on.
And the rest is easy - February (in that year) has 28 days, so you get (28 - 11) + 8 = 25 days.
Related
I was trying to find the number of months between two dates using date_diff() in PHP. As we all know the number of months between 2019-03-01 and 2020-01-31 is 11 months, but the following code return 10 months.
$date1=date_create("2019-03-01");
$date2=date_create("2020-01-31");
$diff=date_diff($date1,$date2);
echo $diff->format("%m months");
Output
10 months
Why this code return 1 month less?
If you need the difference in months from the beginning of the first day to the end of the last day at midnight, you can also set the end date to midnight (24h !) or add a day.
<?php
$dateStart = date_create("2019-03-01");
$dateEnd = date_create("2020-01-31");
//set Time to midnight or add a day
$dateEnd->setTime(24,0,0);
$diff = date_diff($dateStart,$dateEnd);
echo $diff->format("%m months");
//11 months
try self.
The difference is 10 months and 30 days, which is what date_diff() returns:
object(DateInterval)#3 (16) {
["y"]=>
int(0)
["m"]=>
int(10)
["d"]=>
int(30)
["h"]=>
int(0)
["i"]=>
int(0)
["s"]=>
int(0)
["f"]=>
float(0)
["weekday"]=>
int(0)
["weekday_behavior"]=>
int(0)
["first_last_day_of"]=>
int(0)
["invert"]=>
int(0)
["days"]=>
int(336)
["special_type"]=>
int(0)
["special_amount"]=>
int(0)
["have_weekday_relative"]=>
int(0)
["have_special_relative"]=>
int(0)
}
ETA as #showdev commented above.
How to create split second DateInterval in PHP?
For example:
0.123456 sec
?
interval_spec of DateInterval::__construct
does not have F nor f similarly to DateInterval::format that supports split seconds.
Relative format listing of DateInterval::createFromDateString also does not mention any fraction of a sec.
But the DateInterval class properties listing shows:
f
Number of microseconds, as a fraction of a second.
I was able to get DateInterval with a split second by using the DateTime::diff of two DateTimes with a split seconds
example:
$formatStr = 'Y-m-d H:i:s.u';
$dateTimeStr = '2000-01-01 00:00:00.0';
$timeZone = new \DateTimeZone('UTC');
$timerDateTimeStart = \DateTimeImmutable::createFromFormat($formatStr, $dateTimeStr, $timeZone);
$formatStr = 'Y-m-d H:i:s.u';
$dateTimeStr = '2000-01-01 00:00:00.123456';
$timeZone = new \DateTimeZone('UTC');
$timerDateTimeEnd = \DateTimeImmutable::createFromFormat($formatStr, $dateTimeStr, $timeZone);
$timerDiff = $timerDateTimeStart->diff($timerDateTimeEnd);
$intervalStr = $timerDiff->format('%Y-%M-%D %H:%I:%S.%f');
echo 'Interval (yyyy-mm-dd hh:mm:ss.sfract): ' . $intervalStr;
gives:
Interval (yyyy-mm-dd hh:mm:ss.sfract): 00-00-00 00:00:00.123456
since DateTime supports the split second time_format in its constructor and DateTime::createFromFormat understands
u Microseconds (up to six digits) Example: 45, 654321
BTW: don't you think that u in case of working with the DateTime and F or f in case of DateInterval has a potential for making your day a little bit less boring?
One of my workaround could be to create two DateTime with a split second precision and then diff to get the DateInterval with the same split second precision but I would like to get the same result with just the DateInterval.
Do you know how to create DateInterval having 0.123456 sec by using just the DateInterval?
Solution based on the accepted answer:
$dateInterval = DateInterval::createFromDateString("1 day, 2 hours, 3 minutes, 1 second, 123456 microsecond");
$intervalStr = $dateInterval->format('%D %H:%I:%S.%F');
echo 'Interval (dd hh:mm:ss.sfract): ' . $intervalStr . PHP_EOL;
gives:
Interval (dd hh:mm:ss.sfract): 01 02:03:01.123456
There is a way to do this using DateInterval::createFromDateString:
$di = DateInterval::createFromDateString('123456 microseconds');
var_dump($di);
Output:
object(DateInterval)#1 (16) {
["y"]=>
int(0)
["m"]=>
int(0)
["d"]=>
int(0)
["h"]=>
int(0)
["i"]=>
int(0)
["s"]=>
int(0)
["f"]=>
float(0.123456)
["weekday"]=>
int(0)
["weekday_behavior"]=>
int(0)
["first_last_day_of"]=>
int(0)
["invert"]=>
int(0)
["days"]=>
bool(false)
["special_type"]=>
int(0)
["special_amount"]=>
int(0)
["have_weekday_relative"]=>
int(0)
["have_special_relative"]=>
int(0)
}
Demo on 3v4l.org
From the manual:
time
A date with relative parts. Specifically, the relative formats supported by the parser used for strtotime() and DateTime will be used to construct the DateInterval.
This question already has answers here:
Elegant way to get the count of months between two dates?
(10 answers)
Closed 3 years ago.
I'm trying to find the difference between two dates and display it in months.
$currentDate = date_create(date('Y-m-d'));
$expiryDate = date_create('2022-12-10');
$dateDiff = date_diff($currentDate, $expiryDate);
$formattedDateDiff = $dateDiff->format('%m');
When I var_dump($formattedDateDiff) it just returns 0. Changing the $expiryDate reveals that it seems to be giving the leftover months after the years, i.e. if the expiry date was '2022-01-10', it would give 1, as there's 3 years and 1 month between now and then, whereas I want the full 37 months.
When I var_dump($dateDiff) to see what it is that I'm actually formatting, it returns the following:
object(DateInterval)#830 (16) {
["y"]=>
int(3)
["m"]=>
int(0)
["d"]=>
int(0)
["h"]=>
int(0)
["i"]=>
int(0)
["s"]=>
int(0)
["f"]=>
float(0)
["weekday"]=>
int(0)
["weekday_behavior"]=>
int(0)
["first_last_day_of"]=>
int(0)
["invert"]=>
int(0)
["days"]=>
int(1096)
["special_type"]=>
int(0)
["special_amount"]=>
int(0)
["have_weekday_relative"]=>
int(0)
["have_special_relative"]=>
int(0)
}
So it's obviously expecting every relevant unit to be displayed, rather than just one. Is there a way to return the total number of months in the $dateDiff object, rather than just the leftover months? I could just divide the days by 30, but that seems like such a naff way around it, and wouldn't exactly be the most accurate what with differing lengths of months and such.
Cant' you just multiply the years in difference for 12?
return $dateDiff->m + ($datediff->y * 12);
This question already has answers here:
How to calculate the difference between two dates using PHP?
(34 answers)
Closed 5 years ago.
I have this php code to calculate number of days between current date and specific date.
<?php
$query = mysql_query("SELECT * FROM library_users LEFT JOIN students ON library_users.student_id = students.student_id LEFT JOIN books ON library_users.book_id = books.book_id WHERE library_users.student_id <> ''")or die(mysql_error());
while($library_users = mysql_fetch_array($query))
{
$datefrom = date('d-m-Y');
$dateto = $library_users['return_date'];
$datefrom = DateTime::createFromFormat('d-m-Y', $datefrom);
$dateto = DateTime::createFromFormat('d-m-Y', $dateto);
$date_dur = $datefrom->diff($dateto);
$days = $date_dur->format('%d');
}
For now the current date is 27-06-2017; when I input tomorrow's date which is 28-06-2017 I'm getting 1 day which is fine, but when I input 27-07-2017(next month) it is giving me 0 days. How can I solve this?
The problem is that you are using the %d format to get the days, but this will give you the difference in days of a month.
Use %a instead:
$days = $date_dur->format('%a');
http://php.net/manual/en/dateinterval.format.php
a ==> Total number of days as a result of a DateTime::diff()
It works as designed.
DateTime::diff() returns an object of type DateInterval.
A DateInterval object stores the interval in years and subdivisions (months, days, hours, minutes and seconds). You tell it to format() itself using the format "%d".
The %d format specifier means the number of days; not the absolute number of days in the interval but only the days of the last incomplete month.
The format to display the total number of days is "%a":
$days = $date_dur->format('%a');
dumping your object between 27-06-2017 and 27-07-2017 give me this:
object(DateInterval)#4 (15) { ["y"]=> int(0) ["m"]=> int(1) ["d"]=> int(1) ["h"]=> int(0) ["i"]=> int(0) ["s"]=> int(0) ["weekday"]=> int(0) ["weekday_behavior"]=> int(0) ["first_last_day_of"]=> int(0) ["invert"]=> int(0) ["days"]=> int(31) ["special_type"]=> int(0) ["special_amount"]=> int(0) ["have_weekday_relative"]=> int(0) ["have_special_relative"]=> int(0) }
so your are right : it gives you 0 days but 1 month (["m"]=> int(1))
You must use:
$date_dur->days
to get the real days number.
regards.
How can I tell if a variable that contains a stampdate like this “2015-05-12 15:32:53” is less or more than an hour comparing it with current timestamp? Is there any good function in PHP that I can use to compare with this time format?
I have this code:
$date = date('Y-m-d h:i:s a', time()); //Current time e.g. "“2015-05-12 13:32:53”
$timetocomp; //Contains “2015-05-12 15:32:53”
My question is how I can compare them to know if 2 hours have passed or not.
Try DateTime
$timetocomp = "2015-06-27 09:10:53"; // its 2015-05-28 08:5x:xx now
$dateTimeToCompare = DateTime::createFromFormat('Y-m-d H:i:s', $timetocomp);
var_dump($dateTimeToCompare);
$dateTimeNoW = new DateTime();
$diff = $dateTimeToCompare->diff($dateTimeNoW);
if($diff->h >= 1 or $diff->d > 0 or $diff->m > 0 or $diff->y > 0) {
echo "difference of at least 1 hour";
} else {
echo "difference less than 1 hour";
}
Edit: My mistake was that I assumed $diff would contain the difference for each size, but its seperated as var_dump($diff) showed.
object(DateInterval)#3 (15) {
["y"]=> int(0)
["m"]=> int(0)
["d"]=> int(30)
["h"]=> int(0)
["i"]=> int(15)
["s"]=> int(13)
["weekday"]=> int(0)
["weekday_behavior"]=> int(0)
["first_last_day_of"]=> int(0)
["invert"]=> int(1)
["days"]=> int(30)
["special_type"]=> int(0)
["special_amount"]=> int(0)
["have_weekday_relative"]=> int(0)
["have_special_relative"]=> int(0)
}
So I updated the If-clause to also chck the days, months and years.
If you are trying to check weather string contains that format or not, try using regular expression and then try figure out the actual date and comparing.