symfony と 2038年問題
PHPの日付/時間操作で使用されるUNIXタイムスタンプは、皆さんご存知のとおり、
2038年1月19日 12:14:07 までしか扱えません。2038年問題ですね。
この問題を知ってから、開発時はなるべくUNIXタイムスタンプを使用しないように しています。
PHP5.2以降は、この問題をクリアしているDateTimeオブジェクトが使用できるので、
symfonyでの開発時はこれを利用しています。
で、symfony自体はどうかというと・・・
対応してませんでしたorz
というか、symfony1.3から標準のORMとなったDoctrine自体がNG!
このままでは、2038年以降の日付を扱うシステムを開発する際に問題になるので
解決することに。
対象となるバージョン: symfony 1.4.2 + doctrine 1.2
まずはDoctrineの方から。
モデルを作成した際に作成されるBaseクラスは、sfDoctrineRecordという抽象クラスを継承します。
これは、Doctrine_Recordというクラスを継承していますが、このクラス内の_isValueModified()内で、
UNIXタイムスタンプを使用しています。
(1526行目) return strtotime($old) !== strtotime($new);
1./libフォルダ以下に、sfDoctrineRecordを継承した抽象クラスを作成し上記関数を
overrideします。
abstract class myDoctrineRecord extends sfDoctrineRecord { // override protected function _isValueModified($type, $old, $new) { if ($new instanceof Doctrine_Expression) { return true; } if ($type == 'timestamp' || $type == 'date') { // 2038年問題対応 // 5/8修正 //return date_create($old)->format(DATE_ISO8601) // !== date_create($new)->format(DATE_ISO8601); $old_val = null === $old ? false : date_create($old)->format(DATE_ISO8601);
$new_val = null === $new ? false : date_create($new)->format(DATE_ISO8601);
return $old_val !== $new_val; } else { return parent::_isValueModified($type, $old, $new); } } }
2./config/ProjectConfiguration内に、以下の関数を追加
public function configureDoctrine(Doctrine_Manager $manager) { // 追加したクラス名 $options = array('baseClassName'=>'myDoctrineRecord'); sfConfig::set('doctrine_model_builder_options', $options); }
モデルの再作成すれば、Baseクラスの継承元が上記クラスに変更されます。 次に、widgetの対応をします。 (日付のwidgetにsfWidgetFormDateを利用している場合) 1./lib/以下に、sfWidgetFormDateを継承したクラスを作成し、render()を そのままコピーしoverrideします。 class myWidgetFormDate extends sfWidgetFormDate { // override public function render($name, $value = null, $attributes = array(), $errors = array()) { ~省略 (71行目) // 2038年問題対応 // $value = (string) $value == (string) (integer) $value ? // (integer) $value : strtotime($value); // 5/8 修正 // $value = date_create($value); $value = null === $value ? false : date_create($value); ~省略 (78行目) // 2038年問題対応 // $value = array('year' => date('Y', $value), // 'month' => date('n', $value), // 'day' => date('j', $value)); $value = array('year' =>$value->format('Y'), 'month' => $value->format('n'), 'day' => $value->format('j')); ~省略 return strtr($this->getOption('format'), $date); } }
これで、2038年以降の日付も登録できるようになります。
(widgetが日付ではなく日時の場合は、sfWidgetFormTimeも同じようなクラスを作成しなければいけません。
その上で、sfWidgetFormDateTime内のgetTimeObject()で新クラスを返すようにすればOKだと思います。)
まだ他にもあるかもしれませんが、今回はここまで。
後は、気づいたときに修正していきたいと思います。
※ 5/8 追記 : DateTimeオブジェクトを作成し、処理する箇所にバグがあったので修正しました。
myDoctrineRecord・myWidgetFormDateクラス内で「5/8修正」と記述している箇所です。
原因: date_create($value) で、$valueがnullの場合、falseではなく現在日時が返る仕様
でした・・・。
この投稿へのトラックバック
トラックバックはありません。
- トラックバック URL
この投稿へのコメント