Working with DateTime in Doctrine 2 and Zend Framework 2

The problem

Storing datetimes can be a issue if servers and users of your application are distributed around the World and use different time zones. Each user want to work on datetimes in his specific time zone, servers automatically stores values in their time zone, etc. Storing data without operating right conversion will cause strange behaviours.

The solution

The solution is very simple: store all datetimes in UTC time zone and show to each user in his proper time zone. This is, gnerally, a well documented technique, but the following code will explain how to realize this operation in Doctrine 2 and Zend Framework 2 environment. To do this, we will assume you just have a working ZF2 project with a configured DoctrineORMModule (refer to official documentation to reach this achievement).

First of all, lets create a custom Doctrine Datatype to handle DateTime object in UTC. Field of this type will automatically convert value into UTC time zone before store it in DB. The following code is a modification of official doctrine Working with DateTime instances article that at this moment (2013-03-12) has an error in usage of DateTime format() method. Please, note that you can put this file where you prefer inside /module/Application/src/Application/.

/module/Application/src/Application/DBAL/Types/UTCDateTimeType.php:

<?php
namespace ApplicationDBALTypes;

use DoctrineDBALPlatformsAbstractPlatform;
use DoctrineDBALTypesConversionException;

class UTCDateTimeType extends DoctrineDBALTypesDateTimeType
{

     static private $utc = null;

     /**
      * @param DateTime $value
      * @param DoctrineDBALPlatformsAbstractPlatform $platform
      * @return string
      */
     public function convertToDatabaseValue($value, AbstractPlatform $platform)
     {
        if ($value === null) {
            return null;
        }
        $formatString = $platform->getDateTimeFormatString();

        $value->setTimezone((self::$utc) ? self::$utc : (self::$utc = new DateTimeZone('UTC')));

        $formatted = $value->format($formatString);

        return $formatted;
    }

    /**
     * @param string $value
     * @param DoctrineDBALPlatformsAbstractPlatform $platform
     * @return DateTime|mixed|null
     * @throws DoctrineDBALTypesConversionException
     */
    public function convertToPHPValue($value, AbstractPlatform $platform)
    {
        if ($value === null) {
            return null;
        }

        $val = DateTime::createFromFormat(
            $platform->getDateTimeFormatString(),
            $value,
            (self::$utc) ? self::$utc : (self::$utc = new DateTimeZone('UTC'))
        );
        if (!$val) {
            throw ConversionException::conversionFailed($value, $this->getName());
        }
        return $val;
    }
}

To make this Datatype work in a Zend Framework 2 project, we need to load it in our DoctrineORMModule configuration. So let’s edit ZF2 module configuration file by adding the following code:

/module/Application/config/module.config.php:

return array(
    ...
    'doctrine' => array(
        'configuration' => array(
            'orm_default' => array(
                ...
                'types' => array(
                    'utcdatetime' => 'ApplicationDBALTypesUTCDateTimeType',
                ),
                ...
            )
        ),
    ),
    ...
);

At this point we can use Datatype UTCDateTime to map DateTime property of our entities like in the following example. Please note that the location of entities depends on your DoctrineORMModule configuration.

/module/Application/src/Application/Entity/Event.php:

/**
 * @Entity
 * @Table(name="myEvent)
 */
class Event
{
    ...

    /**
     * @Column(type="UTCDateTime")
     * @var DateTime
     */
     protected $datetime;

    ...
}

Now, when we use $event->setDatetime() method, the value will be stored in DB in UTC time zone. For this reason, we need to re-convert it into user time zone before show.

After retrieved an object that contains a DateTime property, let’s set timezone before show with code like:

...
echo $event->getDatetime()->setTimezone(new DateTimeZone('Europe/Rome'))->format('d/m/Y H:i');
...

Please note that time zone and format strings can be retrieved from user preference or automatically detected.

Conclusion

At this point we can simply work with DateTime in Doctrine 2 + Zend Framework2 environment.

Please, keep in mind that this is only an example and can be heavly improved.

Articoli correlati

  • Hi,

    is it your post true now ? I can’t find the documentation state at 2013-03-12
    Thanks

    • Valerio Galano

      Hi Gounlaf,
      2013-03-12 is date when I wrote article, so you will not find a Doctrine documentation updated to this date.
      Today, I visited correspondent link and filter seems still wrong.
      Please, consider to use my modified version.

      • Hi Valerio,

        Thanks for you answer !
        So, i will use your fix.

        But i still have a “question” : if i understand, i have to set the timezone once after retrieved the object from DB. Is it true ?

        • Valerio Galano

          Yes. You have to set timezone based on users settings or user worldwide position.