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
この投稿へのコメント