質問

>> $start_dt = new DateTime()
DateTime::__set_state(array(
   'date' => '2012-04-11 08:34:01',
   'timezone_type' => 3,
   'timezone' => 'America/Los_Angeles',
))
>> $end_dt = new DateTime()
DateTime::__set_state(array(
   'date' => '2012-04-11 08:34:06',
   'timezone_type' => 3,
   'timezone' => 'America/Los_Angeles',
))
>> $start_dt->setTimestamp(strtotime('31-Jan-2012'))
DateTime::__set_state(array(
   'date' => '2012-01-31 00:00:00',
   'timezone_type' => 3,
   'timezone' => 'America/Los_Angeles',
))
>> $end_dt->setTimestamp(strtotime('1-Mar-2012'))
DateTime::__set_state(array(
   'date' => '2012-03-01 00:00:00',
   'timezone_type' => 3,
   'timezone' => 'America/Los_Angeles',
))
>> $interval = $start_dt->diff($end_dt)
DateInterval::__set_state(array(
   'y' => 0,
   'm' => 0,
   'd' => 30,
   'h' => 0,
   'i' => 0,
   's' => 0,
   'invert' => 0,
   'days' => 30,
))
>> $interval->format('%mm %dd')
'0m 30d'

i.e., 31-Jan-2012 to 1-Mar-2012 yields less than a month! I'd expect the output to be 1 month, 1 day. It shouldn't matter the number of days in February; that's the point of using a time library -- it's supposed to handle these things. WolframAlpha agrees.

Should I file a bug to PHP? Is there a hack/fix/workaround to get months to work as expected?

役に立ちましたか?

解決

Should I file a bug to PHP?

No.

The "month" part of the interval means that the month part of the start date can be incremented by that many months. The behaviour in PHP, taking your start date of 31-Jan-2012 and incrementing the month (literally, 31-Feb-2012) and then correcting for a valid date (PHP does this for you) would give 02-Mar-2012 which is later than the target date that you are working with.

To demonstrate this, take your start date and add n months for a few months to see the behaviour.

31-Jan-2012 (Interval)
02-Mar-2012 (P1M)
31-Mar-2012 (P2M)
01-May-2012 (P3M)
31-May-2012 (P4M)
01-Jul-2012 (P5M)

You can see that the month is being incremented, then adjusted to make a valid date.

他のヒント

Updated answer

This behavior of DateTime::diff is certainly unexpected, but it's not a bug. In a nutshell, diff returns years, months, days etc such that if you did

$end_ts = strtotime('+$y years +$m months +$d days' /* etc */, $start_ts);

you would get back the timestamp that corresponds to end original end date.

These additions are performed "blindly" and then date correction applies (e.g. Jan 31 + 1 month would be Feb 31, corrected to Mar 2 or Mar 3 depending on the year). In this specific example you cannot add even one month as salathe also explains.

ライセンス: CC-BY-SA帰属
所属していません StackOverflow
scroll top