Author: | Shogo Kawahara <kawahara@bucyou.net> Twitter: @ooharabucyou |
---|---|
Date: | 2011-12-10 |
License: | Creative Commons Attribution 3.0 Unported License |
この記事は Symfony Advent Calendar 2011 の記事です。
複数の国で展開するようなウェブアプリケーションではタイムゾーンへの対応が必要になることがあります。
Symfony2 ではお世話になることはないでしょうが、Doctrine1 にて timezone に対応する方法も軽く紹介。
Doctrine1 では、datatime 型の属性は、 Y-m-d H:i:s の形式で帰ってくるものでした。 即ち、タイムゾーンに対応する方法として挙げられていたのは、保存や取得の直前に、 date.timezone の値を変え、取得できたら元の date.timezone に戻すという方法でした。
Doctrine Timestamps and User Timezones
なかなか、アグレッシブな方法ですね!
Doctrine2 には、嬉しいことに datetime型は DataTime の instance で帰ってきます。
計算機能などに若干問題がある気がしますが 文字列を素で使うよりは便利です。
さらに、タイムゾーン情報を持つことができます。
Doctrine2 の DBAL には datetimetz型が用意されています。
これは、時刻情報と同時にタイムゾーン情報も記録できる優れものですが、 残念なことに、MySQL、Sqlite では利用できません。
15. Known Vendor Issues - Doctrine DBAL v2.1.0 documantion
MySQL と Doctrine2 を利用して、複数タイムゾーンに対応したような アプリケーションを作るには一手間加える必要がありそうです。。
データベースに保存する値について、固定のタイムゾーンになるような Type を新しく 定義してしまおうという方法があるでしょう。
Symfony2 であれば、あなたのバンドルに新たに以下のような UTCDateTimeType クラスなどをつくります。
<?php
namespace YourBundle\DoctrineExtensions\DBAL\Types;
use Doctrine\DBAL\Types\DateTimeType;
use Doctrine\DBAL\Platforms\AbstractPlatform;
use Doctrine\DBAL\Types\ConversionException;
class UTCDateTimeType extends DateTimeType
{
static private $utc = null;
public function convertToDatabaseValue($value, AbstractPlatform $platform)
{
if ($value === null) {
return null;
}
if (is_null(self::$utc)) {
self::$utc = new \DateTimeZone('UTC');
}
$value->setTimeZone(self::$utc);
return $value->format($platform->getDateTimeFormatString());
}
public function convertToPHPValue($value, AbstractPlatform $platform)
{
if ($value === null) {
return null;
}
if (is_null(self::$utc)) {
self::$utc = new \DateTimeZone('UTC');
}
$val = \DateTime::createFromFormat($platform->getDateTimeFormatString(), $value, self::$utc);
if (!$val) {
throw ConversionException::conversionFailed($value, $this->getName());
}
return $val;
}
}
この型は、データベースに保存するときは、必ず UTC で保存するというものです。 取得時は、UTCのタイムゾーンの日時として、 \DateTime のインスタンスを作ります。 あとの動きは、通常の datetime と同様です。
そして、それを使えるようにしてやるだけです。
app/config/config.yml の doctrine.dbal.types の値をいじくってやります。
doctrine:
dbal:
driver: %database_driver%
(省略)
charset: UTF8
types:
utcdatetime: YourBundle\DoctrineExtensions\DBAL\Types\UTCDateTimeType #追加
これだけで、 utcdatetime 型が使えるようになりますぞ。
もちろん、表示前に DateTime::setTimeZone() で、利用者側で見たいタイムゾーンを 設定してやる必要があるのは注意。