アシアルブログ

アシアルの中の人が技術と想いのたけをつづるブログです

DateTimeクラスを使ったモダンな日付処理

こんにちは。小川です。

今回はPHPのDateTimeクラスについてご紹介します。

-----------------------------------
最初に告知です。日本Symfonyユーザー会を設立しました!
ユーザー会の活動として、

- 入門者向けのサポート活動
- メーリングリストの運営
- ドキュメントの整理
- 公式ドキュメントやブログの翻訳
- 勉強会などのイベントの開催

などを行っていきます。また、昨晩に第1回 Symfonyユーザー会IRC集会を行いました。今後も定期的にIRCで集会を行います。内容はsymfonyに関する質問にお答えしたり、議論をしたりなど様々です。次回は6/20(日)の21時~23時開催を予定していますので、ぜひぜひご参加ください!

※ちなみにアシアルとしては金銭の管理やサーバーの提供などを行っています。Webサイトのデザインもアシアルのデザイナーが行いました。
-----------------------------------

さて本題です。皆さんDateTimeクラスはご存知ですか?PHPをお使いの方ならばdate()関数はよく使われていると思います。DateTimeは日付をオブジェクトとして扱うためのクラスで、PHP 5.2.0以降から利用可能です。例えば次のようなコードがあったとします。



<?php

$date = date('Y-m-d', strtotime('+3 months'));
echo $date . "\n";
// => 2010-09-07


これをDateTimeオブジェクトに置き換えると次のようになります。



<?php

$dt = new DateTime('+3 months');
echo $dt->format('Y-m-d') . "\n";
// => 2010-09-07


次に、特定の日付から3ヵ月引いた日付を出す例です。



<?php

$date = date('Y-m-d', strtotime('2010-09-18 -3 months'));
echo $date . "\n";
// => 2010-06-18


DateTimeを使うと次のようになります。下の例はPHP 5.3.0以降で利用可能なDateIntervalクラスを用いた例です。



<?php

$dt = new DateTime('2010-09-18 -3 months');
echo $dt->format('Y-m-d') . "\n";
// => 2010-06-18

// PHP 5.3.0以降
$dt = new DateTime('2010-09-18');
$interval = DateInterval::createFromDateString('-3 months');
echo $dt->add($interval)->format('Y-m-d') . "\n";
// => 2010-06-18


DateTimeなら2038年問題も大丈夫です。



<?php

$date = date('Y-m-d', strtotime('2038-04-01'));
echo $date . "\n";
// 1970-01-01

$dt = new DateTime('2038-04-01');
echo $dt->format('Y-m-d') . "\n";
// 2038-04-01


DateTimeではDateTimeZoneクラスを用いてタイムゾーンを設定することも可能です。DateTimeZoneはDateTimeのコンストラクタの第2引数にも指定可能です。



<?php

$dt = new DateTime();

// Asia/Tokyo
echo $dt->setTimeZone(new DateTimeZone('Asia/Tokyo'))
    ->format('Y-m-d H:i') . "\n";
// 2010-06-07 15:40

// Europe/London
echo $dt->setTimeZone(new DateTimeZone('Europe/London'))
    ->format('Y-m-d H:i') . "\n";
// 2010-06-07 07:40


2つのDateTimeのdiffをとることも可能です。



<?php

$dt1 = new DateTime('2010-06-18');
$dt2 = new DateTime('2010-06-07');
echo $dt1->diff($dt2)->format('誕生日まであと %a 日') . "\n";
// 誕生日まであと 11 日


DateTime::diff()メソッドは2つのDateTimeの差分をDateIntervalオブジェクトとして返します。そのためこれはPHP 5.3.0以降から使える機能になります。

--------------------------------
Error401さんよりコメントいただき、一部の環境でDateTime::diff()にバグがあるようです。詳細はコメントをご覧ください。
--------------------------------

日付のイテレーションを行うDatePeriodクラスもあります。次の例は6月1日から6月30日までを1日ずつループする例です。これもPHP 5.3.0からの機能です。



<?php

$period = new DatePeriod(
    new DateTime('first day of 2010-06'),
    new DateInterval('P1D'),
    new DateTime('last day of 2010-06 +1 second')
);

foreach ($period as $day) {
    echo $day->format('Y-m-d') . "\n";
}
// 2010-06-01
// 2010-06-02
//  (中略)
// 2010-06-29
// 2010-06-30


ちょっとだけ気を付けなければならないのが new DateTime('last day of 2010-06') とすると6月30日の日付がとれるのですが、時間が00:00:00のままだとイテレーションの対象にならないようです。ですので
+1 secondsとして1秒だけ追加してあげています。(バグ?仕様?)

---------------------
追記。上記の件でもしかしてバグ?とか思って、bugs.php.netにレポートしてみました。
#52015 Ending date is ignored while iterating DatePeriod
書いた後、後方互換性が失われるので、きっとオプションで対応することになるのかなと思ったら案の定その方向で対応されるそうです。
---------------------


手短でしたが、以上がDatetimeクラスおよびそれに関連するクラスの紹介になります。詳細はPHPマニュアルをご覧ください。

いくつかサンプルを出しましたが、DateTimeを使ったからといって記述が短くなるとかそういった話ではありません。重要なのは、日付をオブジェクトとして扱えるということです。
オブジェクトの状態として日付を保持しているのであれば、フォーマットに依存せずに日付を処理することが可能です。

PHP 5でオブジェクト指向機能の強化が行われて以降、SPLやPDOを筆頭にPHP内部のクラスが充実していっています。オブジェクトで扱えると操作しやすくていいですね。今後もこういったオブジェクト指向機能の強化や、クラスの拡張などが行われていくと思われます。今後のPHPの流れに乗り遅れないためにもぜひ皆さんDateTimeを活用していきましょう!

※アシアルのPHPスクールに最近オブジェクト指向編ができましたので、オブジェクト指向がよくわからない方はぜひ受講してください。講師は僕が務めさせていただきます。

--------
ちなみに6月18日は僕の誕生日です。今年で23歳になります。よく「28くらいですか?」とか言われるのですが全然そんなことはないです。アシアルの正社員の中でもまだ最年少をキープしています。
ここまでやってこれたのも、アシアルの社員の方々に色々ご指導いただいたり、勉強会などで多くの方々に色々教えていただいたおかげです。
皆さま今後とも、ご指導ご鞭撻のほどよろしくお願いします!。