问题
I have an issue with a difference of two Datetime. Here is the command line to display the DateInterval object :
php -r "\$a = new Datetime('first day of 4 months ago midnight'); \$b = new Datetime('first day of 1 month ago midnight'); var_dump(\$a->diff(\$b));"
And here the DateInterval output :
class DateInterval#3 (15) {
public $y => int(0)
public $m => int(3)
public $d => int(3)
public $h => int(0)
public $i => int(0)
public $s => int(0)
public $weekday => int(0)
public $weekday_behavior => int(0)
public $first_last_day_of => int(0)
public $invert => int(0)
public $days => int(92)
public $special_type => int(0)
public $special_amount => int(0)
public $have_weekday_relative => int(0)
public $have_special_relative => int(0)
}
Edit: The first and second Datetime:
class DateTime#1 (3) {
public $date =>
string(19) "2014-03-01 00:00:00"
public $timezone_type =>
int(3)
public $timezone =>
string(13) "Europe/Zurich"
}
class DateTime#2 (3) {
public $date =>
string(19) "2014-06-01 00:00:00"
public $timezone_type =>
int(3)
public $timezone =>
string(13) "Europe/Zurich"
}
Notice the 3 days! I'm on PHP 5.5.8 but I'm sure that this DateInterval had 0 months a few days ago. The DateInterval output 0 days in PHP 5.4.28 and the 5.5.14. I'm not sure that the PHP version has an effect.
In both cases, the days property is 92.
回答1:
Providing insight into Paul T. Rawkeen's answer, the problem with DateTime::diff
is that it first converts the timezone to UTC before computation.
<?php
$zurich = new DateTimeZone('Europe/Zurich');
$utc = new DateTimeZone('UTC');
$a = new DateTime('first day of 4 months ago midnight',$zurich);
$b = new DateTime('first day of 1 month ago midnight',$zurich);
var_dump($a,$b);
$a->setTimezone($utc);
$b->setTimezone($utc);
var_dump($a,$b);
?>
Gives the following:
object(DateTime)[3]
public 'date' => string '2014-03-01 00:00:00' (length=19)
public 'timezone_type' => int 3
public 'timezone' => string 'Europe/Zurich' (length=13)
object(DateTime)[4]
public 'date' => string '2014-06-01 00:00:00' (length=19)
public 'timezone_type' => int 3
public 'timezone' => string 'Europe/Zurich' (length=13)
object(DateTime)[3]
public 'date' => string '2014-02-28 23:00:00' (length=19)
public 'timezone_type' => int 3
public 'timezone' => string 'UTC' (length=3)
object(DateTime)[4]
public 'date' => string '2014-05-31 22:00:00' (length=19)
public 'timezone_type' => int 3
public 'timezone' => string 'UTC' (length=3)
The 3 day discrepancy is now very clear, after the timezone is converted from Europe/Zurich
to UTC
the dates are now 2014-02-28 23:00:00
and 2014-05-31 22:00:00
for $a
and $b
respectively.
The solution is to work entirely in UTC and convert before displaying the DateTime:
<?php
$zurich = new DateTimeZone('Europe/Zurich');
$utc = new DateTimeZone('UTC');
$a = new DateTime('first day of 4 months ago midnight',$utc);
$b = new DateTime('first day of 1 month ago midnight',$utc);
var_dump($a,$b);
$a->setTimezone($zurich);
$b->setTimezone($zurich);
var_dump($a,$b);
?>
Notice that all days are now 01
, albeit the hours are now a little different (see the note at the end of this answer):
object(DateTime)[3]
public 'date' => string '2014-03-01 00:00:00' (length=19)
public 'timezone_type' => int 3
public 'timezone' => string 'UTC' (length=3)
object(DateTime)[4]
public 'date' => string '2014-06-01 00:00:00' (length=19)
public 'timezone_type' => int 3
public 'timezone' => string 'UTC' (length=3)
object(DateTime)[3]
public 'date' => string '2014-03-01 01:00:00' (length=19)
public 'timezone_type' => int 3
public 'timezone' => string 'Europe/Zurich' (length=13)
object(DateTime)[4]
public 'date' => string '2014-06-01 02:00:00' (length=19)
public 'timezone_type' => int 3
public 'timezone' => string 'Europe/Zurich' (length=13)
To offer a bit of insight into this phenomenon, note the following:
- February has 28 days (in 2014)
- May has 31 days
- DST starts on March 30: +01:00
- Europe/Zurich is UTC+02:00
When converting from Europe/Zurich to UTC one must consider more than just the year, month and day, but the hours, minutes, and seconds too. If this was any other day than the first of any month, this problem would not occur, however the hours would still be 23:00 and 22:00 (pre the examples above).
回答2:
This thing depends on DateTimeZone
you provide.
If you set Europe/Zurich
or any EEST
time you will get the described result.
If GMT
/UTC
for e.g., you will get $d = 0
.
You can use global time zone definition along your project to avoid such problems (if it suits you)
date_default_timezone_set( "Europe/Zurich" );
or define required time zone for DateTime
objects.
UPD: as was mentioned below in comment, by @mudasobwa, this problem is mentioned here about 3 years ago.
来源:https://stackoverflow.com/questions/24931166/php-wrong-datetimediff-returns-wrong-dateinterval