DT

コンセプト・特徴など

Author : trashtoy

Table of Contents

イミュータブルなクラス設計

このモジュールの時間オブジェクト (特定の日付や日時をあらわすオブジェクト) は, イミュータブル (不変クラス) な設計となっています. 以下のサンプルコードは, 特定の日付とその前後の日付を出力します.

  1. // 既存の方法
  2. $d new DateTime("2012-05-21");
  3. echo $d->format("Y-m-d")// 2012-05-21
  4. $d->modify("+1 day");
  5. echo $d->format("Y-m-d")// 2012-05-22
  6. $d->modiry("-2 day");
  7. echo $d->format("Y-m-d")// 2012-05-20
  8.  
  9. // DT モジュール
  10. $today     new Peach_DT_Date(2012521);
  11. $tomorrow  $today->add("date"1);
  12. $yesterday $today->add("date"-1);
  13. echo $today;     // 2012-05-21
  14. echo $tomorrow;  // 2012-05-22
  15. echo $yesterday// 2012-05-20

既存の方法では modify メソッドを実行するたびにオブジェクトの状態が変化します. 翌日の日付を出力するために「日」のフィールドを 1 進めた後, 今度は前日の日付を出力するために「日」を 2 戻す必要があります. このようにオブジェクトに対する副作用を考慮しながらプログラミングする必要があります.

DT モジュールでは一度生成されたオブジェクトは変化することがありません. add() や set() などでフィールドを操作するたびに新しいオブジェクトが生成されます. プログラマはメソッドの副作用を気にする必要がなくなると共に, 操作後のオブジェクトに対して $yesterday や $tomorrow などの変数名を付けて, 見通しの良いコードを書くことができます.

さらに, 「現在時刻の1年後の1週間前の10:30」といった複雑な操作を, メソッドチェインを使って簡潔に書くことができます.

  1.         ->add("year"1)
  2.         ->add("date"-7)
  3.         ->setAll(array("hour" => 10"minute" => 30));

スコープによるクラスの使い分け

日付だけを扱いたい場合は Peach_DT_Date, 日付に加えて時刻も扱いたい場合は Peach_DT_Datetime といった具合に, 用途に応じて使うクラスを選ぶことが出来ます.

クラスの使い分けが役に立つのは, 例えば日付同士を比較するようなケースです. 既存の方法では, 余計なフィールド同士で比較が行われないよう setTime() で時・分・秒を 0 に揃えるなどの対処が必要でした. DT モジュールの場合は Peach_DT_Date クラスを使うことで同じことが簡潔に表現できます.

  1. // 既存の方法
  2. $subject new DateTime("2012-05-21");
  3. $now     new DateTime();
  4. $now->setTime(000)// 日付だけを比較したいので, 余計な時・分・秒を 0 に揃える
  5. if ($now == $subject{
  6.     // something
  7. }
  8.  
  9. // DT モジュール
  10. $subject new Peach_DT_Date(2012521);
  11. $now     Peach_DT_Date::now();
  12. if ($now->equals($subject)) {
  13.     // something
  14. }

時間操作の API と書式関連の API の分離

書式関連 (parse や format) のロジックは全て Peach_DT_Format インタフェースに移譲しているため, 全体的にシンプルな設計となっています.

Peach_DT_Format の具象クラスをどのように利用するか, 以下のサンプルコードで例を示します. まずは特定の時刻を Last-Modified ヘッダで出力するサンプルです. HTTP-date を扱うクラス Peach_DT_HttpDateFormat を使います.

  1. // タイムゾーンが仮に Asia/Tokyo (UTC+9) に設定されているものとする
  2.  
  3. $time new Peach_DT_Timestamp(201241154321);
  4. header("Last-Modified: " $time->format($f))// "Last-Modified: Sun, 01 Apr 2012 06:43:21 GMT"

次に, サーバー上の任意のファイルの更新日時を時間オブジェクトに変換する例です. 今度は UNIX タイムスタンプを取り扱うためのクラス Peach_DT_UnixTimeFormat を使います.

  1. // タイムゾーンが仮に Asia/Tokyo (UTC+9) に設定されているものとする
  2.  
  3. echo filemtime("/tmp/sample.txt")// 1234567890
  4. $d Peach_DT_Timestamp::parse(filemtime("/tmp/sample.txt")$f);
  5. echo $d->format()// "2009-02-14 08:31:30"

今度は複数のフォーマットを使い分けてみましょう. 以下のようなプログラムを考えます.

  1. とあるファイルの更新日時を調べる.
  2. もしもリクエストヘッダに If-Modified-Since が存在する場合は, ファイルの更新日時と比較する. 2つが同じ時刻だった場合はステータスコード "304 Not Modified" を出力して終了する.
  3. それ以外は, そのファイルの更新日時を Last-Modified ヘッダとして出力し, そのファイルの中身を出力する.

これを実現するためのコードは以下の通りです. Peach_DT_HttpDateFormat が If-Modified-Since のパースと Last-Modified の出力の 2 箇所で利用されていて, Peach_DT_UnixTimeFormat がファイルの更新日時の取得に使われていることに注目してください.

  1. $filename "/tmp/sample.txt";
  2. $modified Peach_DT_Timestamp::parse(filemtime($filename)$uFormat);
  3.  
  4. if (isset($_SERVER["HTTP_IF_MODIFIED_SINCE"])) {  
  5.     $since Peach_DT_Timestamp::parse($_SERVER["HTTP_IF_MODIFIED_SINCE"]$hFormat);
  6.     if ($modified->equals($since)) {
  7.         header("304 Not Modified");
  8.         exit;
  9.     }
  10. }
  11.  
  12. header("Last-Modified: " $modified->format($hFormat));
  13. echo file_get_contents($filename);