brandonlinu/doctrine-utc-bundle
Installation
composer require brandonlinu/doctrine-utc-bundle
Add to config/bundles.php:
return [
// ...
BrandonlinU\DoctrineUtcBundle\DoctrineUtcBundle::class => ['all' => true],
];
Configuration
Override default UTC behavior in config/packages/doctrine_utc.yaml:
doctrine_utc:
default_timezone: 'UTC' # or 'Europe/Paris' if needed
use_utc_for_all: true # forces UTC for all datetime fields
First Use Case
Query a DateTime field in UTC:
$user = $entityManager->getRepository(User::class)->find(1);
$createdAtUtc = $user->getCreatedAt()->format('Y-m-d H:i:s\Z'); // Always UTC
UTC-Only Entities Annotate entities to enforce UTC:
use BrandonlinU\DoctrineUtcBundle\Annotation\UTC;
/**
* @UTC
*/
class User {
/**
* @ORM\Column(type="datetime")
* @UTC
*/
private $createdAt;
}
Timezone-Aware Queries Convert query results to a specific timezone:
$queryBuilder->addSelect('UTC_CONVERT(u.createdAt, :timezone) as createdAtLocal')
->setParameter('timezone', 'America/New_York');
Form Handling
Use UTCType in Symfony Forms:
$builder->add('createdAt', UTCType::class, [
'widget' => 'single_text',
'html5' => true,
'attr' => ['placeholder' => 'YYYY-MM-DD HH:MM:SS (UTC)'],
]);
UTC_NOW() for UTC timestamps:
$qb->where('u.createdAt > UTC_NOW() - INTERVAL 1 DAY');
use JMS\Serializer\Annotation\Type;
use JMS\Serializer\Annotation\SerializedName;
/**
* @Type("DateTime<'Y-m-d\TH:i:s\Z'>")
* @SerializedName("created_at")
*/
public $createdAt;
$this->addSql('ALTER TABLE user ADD created_at DATETIME NOT NULL DEFAULT UTC_TIMESTAMP');
Legacy Data Issues
$entityManager->getConnection()->getDatabasePlatform()->convertToDatabaseValue(
$legacyDate,
DoctrineUtcBundle::UTC_TYPE
);
Timezone Ambiguity in Queries
UTC_CONVERT with raw DateTime objects in the same query. Example of wrong:
// ❌ Ambiguous: Mixes UTC and local time
$qb->where('u.createdAt > :now AND UTC_CONVERT(u.createdAt, :tz) > :nowLocal');
Caching Quirks
# config/packages/doctrine.yaml
doctrine:
orm:
entity_managers:
default:
metadata_cache_driver: array
query_cache_driver: array
result_cache_driver: array
$utcTime = $entity->getCreatedAt();
$localTime = $utcTime->setTimezone(new \DateTimeZone('Europe/Paris'));
dump($utcTime->format('c'), $localTime->format('c')); // Compare outputs
# config/packages/dev/doctrine.yaml
doctrine:
dbal:
logging: true
profiling: true
Look for UTC_CONVERT or UTC_TIMESTAMP in logs.Custom Type Handling
Extend the bundle’s UTCType for custom fields:
use BrandonlinU\DoctrineUtcBundle\Type\UTCType;
class CustomUTCType extends UTCType {
public function convertToDatabaseValue($value, AbstractPlatform $platform) {
if ($value instanceof \DateTimeInterface) {
return $value->setTimezone(new \DateTimeZone('UTC'))->format('Y-m-d H:i:s');
}
return parent::convertToDatabaseValue($value, $platform);
}
}
Register in doctrine_utc.yaml:
doctrine_utc:
custom_types:
custom_utc: BrandonlinU\DoctrineUtcBundle\Type\CustomUTCType
Event Listeners Hook into UTC conversion events:
use BrandonlinU\DoctrineUtcBundle\Event\UTCConvertEvent;
$eventDispatcher->addListener(
UTCConvertEvent::PRE_CONVERT,
function (UTCConvertEvent $event) {
if ($event->getTimezone() === 'UTC') {
$event->setTimezone('America/Los_Angeles'); // Override
}
}
);
Database-Specific Optimizations
AT TIME ZONE for better performance:
$qb->where('u.createdAt AT TIME ZONE :timezone > :cutoff')
->setParameter('timezone', 'UTC')
->setParameter('cutoff', '2023-01-01 00:00:00');
CONVERT_TZ in queries; pre-convert in PHP when possible.How can I help you explore Laravel packages today?