PHP DateTime->diff() doesn't work correctly - php

I met an interesting case, related to the diff() method of DateTime class.
If I try to calculate difference between two dates in months like
$datetime1 = new \DateTime('June 2019');
$datetime2 = new \DateTime('July 2019');
$interval = $datetime1->diff($datetime2);
echo $interval->format('%m');
, as result I get 0.
Why does this happen?
Print_r's:
$datetime1:
DateTime Object ( [date] => 2019-06-01 00:00:00.000000
[timezone_type] => 3 [timezone] => Europe/Berlin )
$datetime2:
DateTime Object ( [date] => 2019-07-01 00:00:00.000000
[timezone_type] => 3 [timezone] => Europe/Berlin )
$interval:
DateInterval Object ( [y] => 0 [m] => 0 [d] => 30 [h] => 0 [i] => 0 [s] => 0 [f] => 0
[weekday] => 0 [weekday_behavior] => 0 [first_last_day_of] => 0
[invert] => 0 [days] => 30 [special_type] => 0 [special_amount] => 0
[have_weekday_relative] => 0 [have_special_relative] => 0 )

There is big inconsistency with timezone and date handing in PHP
This appears to be a bug (in so far as the datetime format is forced to a GMT* offset, according to this comment).
*(but forcing to GMT seems inconsistent with the results established by the code below)
Setting the server timezone value to any timezone does not effect this script timezone anomaly.
Below are two cases showing what happens in different time zones:
Case 1:
The following code will output a list of results for each time zone:
$tzList = DateTimeZone::listIdentifiers(DateTimeZone::ALL);
print "Current Zone:". print_r(ini_get('date.timezone'),true)."<br>\n<BR>\n";
foreach($tzList as $tzRow) {
$tz = new DateTimeZone($tzRow);
//$tz = null;
$datetime1 = new \DateTime('June 2019', $tz);
$datetime2 = new \DateTime('July 2019', $tz);
$interval = $datetime1->diff($datetime2, false);
echo $interval->format('%a %m') . PHP_EOL. " :: ";
print print_r($datetime1->getTimezone(),true)."<BR>";
}
The result of this list output shows a high (~60%) rate of 0 and the rest of 1 month .
Please see here: http://sandbox.onlinephpfunctions.com/code/b18ba13deb94d112b12630a12265363fb6c7670b
Case 2:
Setting the timezone AFTER creating the object, results in a consistent answer (albeit incorrect)
$tzList = DateTimeZone::listIdentifiers(DateTimeZone::ALL);
print "Current Zone:". print_r(ini_get('date.timezone'),true)."<br>\n<BR>\n";
foreach($tzList as $tzRow) {
//$tz = new DateTimeZone($tzRow);
$tz = null;
$datetime1 = new \DateTime('June 2019', $tz);
$datetime2 = new \DateTime('July 2019', $tz);
$datetime1->setTimezone(new DateTimeZone($tzRow));
$datetime2->setTimezone(new DateTimeZone($tzRow));
$interval = $datetime1->diff($datetime2, false);
echo $interval->format('%a %m') . PHP_EOL. " :: ";
print print_r($datetime1->getTimezone(),true)."<BR>";
}
This output's generated here all all 30 days out; but all 0 months difference.
See code here: http://sandbox.onlinephpfunctions.com/code/7bcc62f4e36f41df71b9cb928de75a53f233d9fd
So it's your choice if you want to use sometimes correct results or universally incorrect rbut consistent results, by setting when you establish the Timezone value in the DateTime objects.
Possible Solution:
If the server timezone is correctly set to UTC "correct" timezone (that naturally returns "1" month in Case 1, then CASE 2 above works consistently across all time zones given to the DateTime objects.

The problem is in your timezone.
There is a post explaining about it here.
See this example:
<?php
echo "----- Europe/Berlin -----\n";
date_default_timezone_set('Europe/Berlin');
$datetime1 = new \DateTime('June 2019');
$datetime2 = new \DateTime('July 2019');
print_r($datetime1);
print_r($datetime2);
$interval = $datetime1->diff($datetime2);
print_r($interval);
echo "%m = " . $interval->format('%m') . PHP_EOL;
echo "%a = " . $interval->format('%a') . PHP_EOL;
echo "%s = " . $interval->format('%s') . PHP_EOL;
echo "\n\n\n----- America/Sao_Paulo -----\n";
date_default_timezone_set('America/Sao_Paulo');
$datetime1 = new \DateTime('June 2019');
$datetime2 = new \DateTime('July 2019');
print_r($datetime1);
print_r($datetime2);
$interval = $datetime1->diff($datetime2);
print_r($interval);
echo "%m = " . $interval->format('%m') . PHP_EOL;
echo "%a = " . $interval->format('%a') . PHP_EOL;
echo "%s = " . $interval->format('%s') . PHP_EOL;
And the output:
$ php date_diff.php
----- Europe/Berlin -----
DateTime Object
(
[date] => 2019-06-01 00:00:00.000000
[timezone_type] => 3
[timezone] => Europe/Berlin
)
DateTime Object
(
[date] => 2019-07-01 00:00:00.000000
[timezone_type] => 3
[timezone] => Europe/Berlin
)
DateInterval Object
(
[y] => 0
[m] => 0
[d] => 30
[h] => 0
[i] => 0
[s] => 0
[f] => 0
[weekday] => 0
[weekday_behavior] => 0
[first_last_day_of] => 0
[invert] => 0
[days] => 30
[special_type] => 0
[special_amount] => 0
[have_weekday_relative] => 0
[have_special_relative] => 0
)
%m = 0
%a = 30
%s = 0
----- America/Sao_Paulo -----
DateTime Object
(
[date] => 2019-06-01 00:00:00.000000
[timezone_type] => 3
[timezone] => America/Sao_Paulo
)
DateTime Object
(
[date] => 2019-07-01 00:00:00.000000
[timezone_type] => 3
[timezone] => America/Sao_Paulo
)
DateInterval Object
(
[y] => 0
[m] => 1
[d] => 0
[h] => 0
[i] => 0
[s] => 0
[f] => 0
[weekday] => 0
[weekday_behavior] => 0
[first_last_day_of] => 0
[invert] => 0
[days] => 30
[special_type] => 0
[special_amount] => 0
[have_weekday_relative] => 0
[have_special_relative] => 0
)
%m = 1
%a = 30
%s = 0
In my timezone $interval->format('%m'); is 1.
You can set timezone on your dates to calculate the difference between them.
$datetime1 = new \DateTime('June 2019', new DateTimeZone('UTC'));
$datetime2 = new \DateTime('July 2019', new DateTimeZone('UTC'));
$interval = $datetime1->diff($datetime2);
print_r($interval);
echo "%m = " . $interval->format('%m') . PHP_EOL;
$ php date_diff.php
DateInterval Object
(
[y] => 0
[m] => 1
[d] => 0
[h] => 0
[i] => 0
[s] => 0
[f] => 0
[weekday] => 0
[weekday_behavior] => 0
[first_last_day_of] => 0
[invert] => 0
[days] => 30
[special_type] => 0
[special_amount] => 0
[have_weekday_relative] => 0
[have_special_relative] => 0
)
%m = 1

Can you try it by adding a timezone?
$timezones = [
'UTC',
'Europe/Berlin',
'America/Belize',
'Asia/Hong_Kong',
];
foreach ($timezones as $timezone) {
$tz = new DateTimeZone($timezone);
$datetime1 = new \DateTime('June 2019', $tz);
$datetime2 = new \DateTime('July 2019', $tz);
$interval = $datetime1->diff($datetime2);
echo str_pad($timezone, 20, ' ').' '.$interval->format('months: %M, day: %D, days: %a') . PHP_EOL;
}
Result:
UTC months: 01, day: 00, days: 30
Europe/Berlin months: 00, day: 30, days: 30
America/Belize months: 01, day: 00, days: 30
Asia/Hong_Kong months: 00, day: 30, days: 30

Related

PHP - Hours difference is not accurate using DateTime object

I am trying to get difference in hours between two dates using PHP DateTime object but the result is not accurate. One date is the current date time and second is the event date which comes from database. I don't understand on what logic the hours difference is calculated.
Here's my code:
<?php
$date1 = new DateTime();
$date2 = new DateTime("2018-09-04 20:37:06");
$interval = $date2->diff($date1);
echo "<pre>";
print_r($interval);
echo "</pre>";
echo $interval->format('%a Day and %h hours');
?>
Output:
DateInterval Object
(
[y] => 0
[m] => 0
[d] => 0
[h] => 3
[i] => 14
[s] => 13
[weekday] => 0
[weekday_behavior] => 0
[first_last_day_of] => 0
[invert] => 1
[days] => 0
[special_type] => 0
[special_amount] => 0
[have_weekday_relative] => 0
[have_special_relative] => 0
)
0 Day and 3 hours
The script is executed at 08:53 PM (India) i.e. 20:53:00 but the difference is 3 hours. Don't know why?
For Indian time this should help :-
<?php
$timezone = new DateTimeZone('Asia/Kolkata');
$date1 = new DateTime('now', $timezone);
$date2 = new DateTime("2018-09-04 20:37:06", $timezone);
$interval = $date2->diff($date1);
echo "<pre>";
print_r($interval);
echo "</pre>";
echo $interval->format('%a Day and %h hours');
?>
Just use the DateTimeZone('Zone') and it should set the new date to the zone where you are.

How to show server run time in laravel?

i want to show date to user from how much days website is running
e.g today : runing from 10 days . next day : running from 11 days
$current = time();
$initial_date = strtotime("2017-11-1"); //You will have to fix this
$datediff = $current - $initial_date;
$num_of_days = floor($datediff / (60 * 60 * 24));
echo "Running from ".$num_of_days." days";
Note: This will not count today.
It's not laravel specific, you can use this in PHP generally:
exec("uptime", $uptimeVar);
echo($uptimeVar[0]);
You can also format it any way you like.
This way helpful to you. first define the web site starting date as a constant as follow:
define('START_DATE', '2017-11-02');
Then you have to put below code in to the place you have to show the date difference:
$date1 = new DateTime(START_DATE);
$date2 = new DateTime(date('Y-m-d'));
$diff = $date1->diff($date2);
print_r($diff); // or $diff->days
echo $diff->days . " day(s)"; // output '6 day(s)'
Output looks like:
DateInterval Object
(
[y] => 0
[m] => 0
[d] => 6
[h] => 0
[i] => 0
[s] => 0
[weekday] => 0
[weekday_behavior] => 0
[first_last_day_of] => 0
[invert] => 0
[days] => 6
[special_type] => 0
[special_amount] => 0
[have_weekday_relative] => 0
[have_special_relative] => 0
)
6 day(s)

PHP DateTime diff issue between dates

$start = new DateTime('2014-01-07', new DateTimeZone('UTC'));
$end = clone $start;
$start->sub(new DateInterval('P1M')); // substract one month
echo $start->format('Y-m-d').' - '.$end->format('Y-m-d');
// 2013-12-07 - 2014-01-07 - seems correct, let's get number of days...
print_r($end->diff($start));
// DateInterval Object ( [y] => 0 [m] => 2 [d] => 0 [h] => 0 [i] => 0 [s] => 0 [invert] => 1 [days] => 61 )
61 days??? What is going on in here?
try this,
$diff=date_diff($date1,$date2);

How to get current date/time as a date object in PHP

How do you get today's date, as a date object?
I'm trying to compute the difference between some start date and today. The following will not work, because getdate() returns an array and not a date object:
$today = getdate();
$start = date_create('06/20/2012');
$diff = date_diff($start, $today);
echo($today . '<br/>' . $start . '<br/>' . $diff);
Output:
Array ( [seconds] => 8 [minutes] => 1 [hours] => 16 [mday] => 11 [wday] => 1 [mon] => 6 [year] => 2012 [yday] => 162 [weekday] => Monday [month] => June [0] => 1339455668 )
DateTime Object ( [date] => 2012-06-20 00:00:00 [timezone_type] => 3 [timezone] => America/Los_Angeles )
new DateTime('now');
http://www.php.net/manual/en/datetime.construct.php
Comparing is easy:
$today = new DateTime('now');
$newYear = new DateTime('2012-01-01');
if ($today > $newYear) {
}
Op's edit
I just needed to call date_default_timezone_set, and then this code worked for me.
To get difference in days use this:
$today = new DateTime('today');
the time in this object eill be 00:00:00
If you want difference with hours minutes and seconds use this:
$now = new DateTime('now');
I ended up using the date_create constructor (no parameter) to get the current date.
$diff = date_diff(date_create('06/20/2012'), date_create());
print_r($diff);
Output:
DateInterval Object ( [y] => 0 [m] => 0 [d] => 8 [h] => 6 [i] => 30 [s] => 40 [invert] => 1 [days] => 8 )
I have no idea why, but Mike B's answer (and any constructor I tried for DateTime) threw an error for me in PHP5 / IIS.

Weird issue with PHP's DateTime::add not adding time

I've managed to reduce this to a small script which reproduces the issue (Tried on two different PC's, but both with 5.3.6):
<?php
$item = array('monthly_on_the' => 4);
$date = new DateTime();
$date->modify('first day of this month');
print_r($date);
$interval = new DateInterval('P'.$item['monthly_on_the'].'D');
print_r($interval);
$return = $date->add($interval);
if (!$return) die('Bad stuff happened!');
print_r($date);
DateTime Object
(
[date] => 2012-02-01 17:15:23
[timezone_type] => 3
[timezone] => Australia/Sydney
)
DateInterval Object
(
[y] => 0
[m] => 0
[d] => 4
[h] => 0
[i] => 0
[s] => 0
[invert] => 0
[days] =>
)
DateTime Object
(
[date] => 2012-02-01 17:15:23
[timezone_type] => 3
[timezone] => Australia/Sydney
)
Is this a bug and if so, has it been fixed in more recent versions of PHP? Or is there something weird in the time stuff that I'm overlooking?
Yes it's a bug. I'm experiencing the same thing with PHP 5.3.6. Apparently it's fixed in 5.3.7. See the changelog:
http://www.php.net/ChangeLog-5.php#5.3.7
Bug Report:
https://bugs.php.net/bug.php?id=54340
Workaround (if you can't upgrade):
$date->setTimestamp(strtotime('first day of this month', $date->getTimestamp()));

Categories