15
13

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 5 years have passed since last update.

DateTime::diffの挙動がよくわからない

Last updated at Posted at 2016-03-18

DateTime::diff

http://php.net/manual/ja/datetime.diff.php
ふたつの DateTime オブジェクトの差を返す

サンプル

$d1 = new DateTime('2016-03-18');
$d2 = new DateTime('2017-04-19');
$diff = $d1->diff($d2);
var_dump($diff);
/*
yが年差、mが月差、dが日差を表すので、
1年と1月と1日の差があることを表している。
object(DateInterval)#3 (15) {
  ["y"]=>
  int(1)
  ["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(397)
  ["special_type"]=>
  int(0)
  ["special_amount"]=>
  int(0)
  ["have_weekday_relative"]=>
  int(0)
  ["have_special_relative"]=>
  int(0)
}
*/

ここまでは良い。理解できる。
問題は3月を対象とした場合。

$d1 = new DateTime('2016-01-01');
$d2 = new DateTime('2016-02-01');
$diff = $d1->diff($d2);
echo $diff->m;//1

$d1 = new DateTime('2016-01-01');
$d2 = new DateTime('2016-03-01');
$diff = $d1->diff($d2);
echo $diff->m;//1 !?

閏年のせいというわけでもなく(それだったとしても問題だけど)

$d1 = new DateTime('2015-01-01');
$d2 = new DateTime('2015-02-01');
$diff = $d1->diff($d2);
echo $diff->m;//1

$d1 = new DateTime('2015-01-01');
$d2 = new DateTime('2015-03-01');
$diff = $d1->diff($d2);
echo $diff->m;//1 !?

そもそも閏年であっても3月で無ければ問題ない

$d1 = new DateTime('2016-01-01');
$d2 = new DateTime('2016-04-01');
$diff = $d1->diff($d2);
echo $diff->m;//3 正常

GMTなら3月でも正常表示できる。(なんで?

$d1 = new DateTime('2016-01-01', new DateTimeZone('GMT'));
$d2 = new DateTime('2016-02-01', new DateTimeZone('GMT'));
$diff = $d1->diff($d2);
echo $diff->m;//1

$d1 = new DateTime('2016-01-01', new DateTimeZone('GMT'));
$d2 = new DateTime('2016-03-01', new DateTimeZone('GMT'));
$diff = $d1->diff($d2);
echo $diff->m;//2

ということは、Asia/Tokyoでも9時間足せば正常表示できる?

$d1 = new DateTime('2016-01-01 09:00:00', new DateTimeZone('Asia/Tokyo'));
$d2 = new DateTime('2016-02-01 09:00:00', new DateTimeZone('Asia/Tokyo'));
$diff = $d1->diff($d2);
echo $diff->m;//1

$d1 = new DateTime('2016-01-01 09:00:00', new DateTimeZone('Asia/Tokyo'));
$d2 = new DateTime('2016-03-01 09:00:00', new DateTimeZone('Asia/Tokyo'));
$diff = $d1->diff($d2);
echo $diff->m;//2

出来た。
しっくりこない。

まとめ

  • Asia/Tokyoタイムゾーンでは3月との差分が正しく取得できない、の?
  • 3月以外にもあるかもしれないけど、調べてない。
  • タイムゾーンの影響を受けるのであれば、3月以外では正常に取得できる理由が分からない。
  • PHP5.4.xで確認。PHP7等の最新の挙動を確認できる方がいましたら教えてください。

対策

  • これだけではQiitaは質問サイトじゃねぇぞ、というツッコミを受けるので、対策を。
function datetime_diff_month(DateTime $d1, DateTime $d2, $absolute = false){
  $diff_month = ($d2->format('Y')*12 + $d2->format('n')) - ($d1->format('Y')*12 + $d1->format('n'));
  return $absolute ? abs($diff_month) : $diff_month;
}
$d1 = new DateTime('2016-01-01', new DateTimeZone('Asia/Tokyo'));
$d2 = new DateTime('2016-03-01', new DateTimeZone('Asia/Tokyo'));

echo datetime_diff_month($d1, $d2); //2 (^-^)

力技過ぎる・・・。

15
13
5

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
15
13

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?