elcweb/common-bundle
Elcweb CommonBundle is a small Symfony bundle that adds a UTCDateTimeType Doctrine DBAL type, letting you store all DateTime values as UTC through a simple config.yml entry. Install via Composer and override the datetime type. MIT licensed.
This is a tiny Symfony/Doctrine utility bundle, not a Laravel package. Its documented surface is a single thing: a DBAL type that normalizes DateTime values to UTC when persisting. With 0 stars, 0 dependents, and a last release in November 2020, treat it as a snippet library rather than a maintained dependency.
Minimal steps:
composer show -a elcweb/common-bundle
then browse vendor/elcweb/common-bundle/src/ after install. Look for a *Bundle class (whether it needs registration in config/bundles.php or AppKernel), a DBAL/Types/ directory, and any helpers not mentioned in the README. The README does not show bundle registration, which suggests the DBAL type may work without it — verify.composer require elcweb/common-bundle --with-all-dependencies
If it conflicts with DBAL 3/4 or PHP 8, stop here and copy the class instead (see Gotchas).config/packages/doctrine.yaml (or config.yml on older Symfony):
doctrine:
dbal:
types:
datetime: Elcweb\CommonBundle\DBAL\Types\UTCDateTimeType
datetime column where you want the database to contain UTC regardless of the PHP process timezone. Create an entity, set new \DateTime('2024-01-15 12:00:00', new \DateTimeZone('America/Montreal')), flush, and inspect the raw row — it should read 17:00:00. Then reload the entity and confirm the returned object's timezone is UTC.If you are working in Laravel proper, you almost certainly do not need this bundle. Laravel's equivalent is 'timezone' => 'UTC' in config/app.php, datetime casts on Eloquent models, and converting to the display timezone at the boundary. Only reach for this bundle if you are using Doctrine inside a Laravel or Symfony app.
Global override (what the README recommends). Mapping the existing datetime type name means every entity in the app — including third-party bundles — changes behavior at once. Good for greenfield projects where UTC storage is the house rule from day one.
Named type alias for gradual adoption. Register it under a new name and opt in per column:
doctrine:
dbal:
types:
utc_datetime: Elcweb\CommonBundle\DBAL\Types\UTCDateTimeType
#[ORM\Column(type: 'utc_datetime')]
private \DateTime $createdAt;
This avoids silently changing legacy entities and third-party schemas. Caveat: on DBAL 2, Type::getType() validates that the instance's getName() matches the registered key, so an alias may fail if the class still reports datetime. Test it; if it fails, subclass and override getName().
Entity-level discipline. Store UTC, convert only at the edges:
// Persist/domain: UTC
$order->setCreatedAt(new \DateTimeImmutable('now', new \DateTimeZone('UTC')));
// Presentation/API: convert for the user
$local = $order->getCreatedAt()->setTimezone(new \DateTimeZone($user->getTimezone()));
Add a serializer normalizer or a presenter so controllers never leak UTC strings to clients expecting local time.
Migrations and existing data. Registering the type changes interpretation, not stored bytes. If your table currently holds local-time values, you need a one-off data migration that shifts each row by its offset. Do this before deploying the type change, not after. Keep in mind DST: converting historical local timestamps to UTC requires the offset that applied on that date, not today's offset.
Testing. Unit-test the type directly rather than through a full kernel:
$type = new \Elcweb\CommonBundle\DBAL\Types\UTCDateTimeType();
$platform = new \Doctrine\DBAL\Platforms\MySQL57Platform();
$value = new \DateTime('2024-01-15 12:00:00', new \DateTimeZone('America/Montreal'));
$this->assertSame('2024-01-15 17:00:00', $type->convertToDatabaseValue($value, $platform));
Also add one functional test that flushes and reads back through the entity manager — that catches configuration mistakes the unit test cannot.
If you keep it long term. Because the package is effectively unmaintained, consider vendoring the single class into App\Doctrine\Types\UTCDateTimeType under the MIT license. That lets you fix the mutation issue and add datetime_immutable support without waiting for a release, and removes an abandoned dependency from your audit surface.
$value->setTimezone($utc) inside convertToDatabaseValue(), which silently mutates the caller's DateTime. Code that reuses that object afterward will see a shifted timezone. Verify the source; if it mutates, clone before converting or fix it in a vendored copy.convertToPHPValue(), entities hold UTC DateTime instances. Any view, email template, or API response that formats them directly will show UTC. Centralize conversion.datetime_immutable is a separate type. Overriding datetime does nothing for datetime_immutable columns, so you can end up with mixed semantics in one schema. Check which type your entities and third-party bundles actually use.grep -r "type=\"datetime\"" vendor/ before a global override.TIMESTAMP columns apply session-timezone conversion; DATETIME stores literals. UTC storage is only predictable with DATETIME (or a UTC session timezone). Confirm your platform and column type.doctrine.dbal.types, run bin/console cache:clear (or php artisan config:clear under Laravel) before debugging why nothing changed.convertToPHPValue() returns null for null rather than constructing a date; nullable columns should be tested explicitly.getName(), add immutability handling, or avoid mutation) and registering it under a custom name. There is no meaningful configuration surface beyond the DBAL type name.doctrine:migrations:diff sees the same type name and reports no schema change, so the data conversion is entirely on you — and it's easy to forget in a deployment runbook.How can I help you explore Laravel packages today?