読者です 読者をやめる 読者になる 読者になる

ありがとう。また会おう。

まぁゆるりとやっていきますよと。

DateTimeのインスタンス達はmax(), min(), sort()できる

PHPerのみなさん、DateTimeクラス使ってますか?
意外とまだPHP4由来の関数(date()、strtotime()など)使っているケースもよくみかけるのですが
DateTimeクラス使えばいろいろスマートにプログラム書けるし、例外処理もできる*1んで
これからはこっちを使うようにしましょうよ、と思うのです*2

で、これも意外とまだ知らない人多いようなので触れておくと
PHP5.2.2以降、DateTimeクラスのインスタンス同士を、比較演算子で直接比較できるようになりました。
http://www.php.net/manual/ja/datetime.diff.php
の例2のところに記載されています。

<?php
$date1 = new DateTime("now");
$date2 = new DateTime("tomorrow");

var_dump($date1 == $date2);  //false
var_dump($date1 < $date2);     //true
var_dump($date1 > $date2);     //false

日付の比較って、Webアプリケーション書いていると大抵必要になるシチュエーションだと思うので
この書き方ができることを知ってると、読みやすくスマートにコード書けるんじゃないかなと。

で、さらにこの「DateTimeクラスのインスタンス同士は比較演算子で直接比較できる」ことの帰結として
2つのインスタンスを比較できるということは、もっと多数のインスタンスがあれば、それらも比較できる。
つまり、max(), min(), sort() といった関数にDateTimeクラスのインスタンス(の配列)を渡すこともできます。

<?php
$date1 = new DateTime;
$date2 = new DateTime('Yesterday');
$date3 = new DateTime('Tomorrow');
$dateList = array($date1, $date2, $date3);

//max()
var_dump(max($date1, $date2, $date3)); 
var_dump(max($dateList));
//min()
var_dump(min($date1, $date2, $date3));
var_dump(min($dateList));
//sort()
sort($dateList);
var_dump($dateList);

実行結果

object(DateTime)#3 (3) {
  ["date"]=>
  string(19) "2013-10-22 00:00:00"
  ["timezone_type"]=>
  int(3)
  ["timezone"]=>
  string(10) "Asia/Tokyo"
}
 
object(DateTime)#3 (3) {
  ["date"]=>
  string(19) "2013-10-22 00:00:00"
  ["timezone_type"]=>
  int(3)
  ["timezone"]=>
  string(10) "Asia/Tokyo"
}

object(DateTime)#2 (3) {
  ["date"]=>
  string(19) "2013-10-20 00:00:00"
  ["timezone_type"]=>
  int(3)
  ["timezone"]=>
  string(10) "Asia/Tokyo"
}

object(DateTime)#2 (3) {
  ["date"]=>
  string(19) "2013-10-20 00:00:00"
  ["timezone_type"]=>
  int(3)
  ["timezone"]=>
  string(10) "Asia/Tokyo"
}

array(3) {
  [0]=>
  object(DateTime)#2 (3) {
    ["date"]=>
    string(19) "2013-10-20 00:00:00"
    ["timezone_type"]=>
    int(3)
    ["timezone"]=>
    string(10) "Asia/Tokyo"
  }
  [1]=>
  object(DateTime)#1 (3) {
    ["date"]=>
    string(19) "2013-10-21 01:01:03"
    ["timezone_type"]=>
    int(3)
    ["timezone"]=>
    string(10) "Asia/Tokyo"
  }
  [2]=>
  object(DateTime)#3 (3) {
    ["date"]=>
    string(19) "2013-10-22 00:00:00"
    ["timezone_type"]=>
    int(3)
    ["timezone"]=>
    string(10) "Asia/Tokyo"
  }
}

max()、min()とかは、例えばいくつかの日時候補があって、そのうちもっとも現在日時に近いものを採用したい、というケースで使えます。
ソシャゲでいうと、レイドボスの討伐期限が普通はエンカウントして2時間後なんだけど、イベント終了やメンテナンスがある場合は
その日時と比較して、もっとも現在日時に近いものを討伐期限として採用する、みたいなことができます。

*1:例えば、strtotime('hoge')のように日付書式として正しくない引数が与えられた場合、strtotime()はfalseを返します。DateTimeのコンストラクタはExceptionをthrowします。

*2:PHP5.5からは、インスタンス生成時以外値を変更できない(代わりに新しいいインスタンスを生成して返す)DateTimeImmutableも使えます。個人的にはこちらの方がお勧めです。