PHPackages                             petrknap/zoned-datetime-persistence - PHPackages - PHPackages  [Skip to content](#main-content)[PHPackages](/)[Directory](/)[Categories](/categories)[Trending](/trending)[Leaderboard](/leaderboard)[Changelog](/changelog)[Analyze](/analyze)[Collections](/collections)[Log in](/login)[Sign up](/register)

1. [Directory](/)
2. /
3. [Database &amp; ORM](/categories/database)
4. /
5. petrknap/zoned-datetime-persistence

ActiveLibrary[Database &amp; ORM](/categories/database)

petrknap/zoned-datetime-persistence
===================================

Timezone aware date-time persistence

0.3.7(3mo ago)01.0k↓44.4%[5 PRs](https://github.com/petrknap/zoned-datetime-persistence/pulls)1LGPL-3.0-or-laterPHPPHP &gt;=8.2

Since Nov 6Pushed 3mo agoCompare

[ Source](https://github.com/petrknap/zoned-datetime-persistence)[ Packagist](https://packagist.org/packages/petrknap/zoned-datetime-persistence)[ Docs](https://github.com/petrknap/zoned-datetime-persistence)[ Fund](https://petrknap.github.io/donate.html)[ RSS](/packages/petrknap-zoned-datetime-persistence/feed)WikiDiscussions main Synced 1mo ago

READMEChangelog (10)Dependencies (10)Versions (22)Used By (1)

Timezone aware date-time persistence
====================================

[](#timezone-aware-date-time-persistence)

[![GitHub](https://camo.githubusercontent.com/9e1deb0320a28c606c3f387e7b056e6f8b42a57211cfea7af512f17509160031/68747470733a2f2f696d672e736869656c64732e696f2f6769746875622f762f72656c656173652f706574726b6e61702f7a6f6e65642d6461746574696d652d70657273697374656e63653f696e636c7564655f70726572656c6561736573266c6162656c3d476974487562267374796c653d666c6174)](https://github.com/petrknap/zoned-datetime-persistence/releases)[![JitPack](https://camo.githubusercontent.com/79db6e8dee4170056ca873f339270260ee5efe4423ed9b5a29fe45d344ace3d8/68747470733a2f2f696d672e736869656c64732e696f2f6a69747061636b2f76657273696f6e2f696f2e6769746875622e706574726b6e61702f7a6f6e65642d6461746574696d652d70657273697374656e63653f6c6162656c3d4a69745061636b267374796c653d666c6174)](https://jitpack.io/#io.github.petrknap/zoned-datetime-persistence)[![Packagist](https://camo.githubusercontent.com/fb8d7bd38c387c2c20dbe72301caeabbe89a8893da30655ef9f444f502c4ccd4/68747470733a2f2f696d672e736869656c64732e696f2f7061636b61676973742f762f706574726b6e61702f7a6f6e65642d6461746574696d652d70657273697374656e63653f6c6162656c3d5061636b6167697374267374796c653d666c6174)](https://packagist.org/packages/petrknap/zoned-datetime-persistence)

Many data storage systems (like MySQL) do not natively support storing timezone information alongside date-time values. This limitation introduces ambiguity when handling zoned date-times — particularly in applications operating across multiple timezones or even within a single timezone that observes multiple offsets (e.g. due to daylight saving time).

This package addresses the issue by providing tools that treat zoned date-time as a pair consisting of:

- the UTC date-time value, and
- a companion value that explicitly captures the corresponding timezone information.

Implemented
-----------

[](#implemented)

- [UTC with local date-time](#utc-with-local-date-time)
    - [How to use it](#how-to-use-it)
- [UTC with timezone](#utc-with-timezone)
    - [UTC with system timezone](#utc-with-system-timezone)
    - [UTC date-time converter / type / cast](#utc-date-time-converter--type--cast)

### UTC with local date-time

[](#utc-with-local-date-time)

> `UtcWithLocal`

The **most useful** approach is to store the **UTC date-time together with its local counterpart**. This dual representation enables **seamless manipulation** of date-time values directly **within storage system**. The local date-time is ideal for grouping and filtering based on user or business context, while the UTC value ensures consistent and accurate sorting across timezones.

#### How to use it

[](#how-to-use-it)

There is built-in support for the **Jakarta Persistence API** (see [`Note.java`](./src/test/java/some/Note.java) and [`JpaTest.java`](./src/test/java/io/github/petrknap/persistence/zoneddatetime/JpaTest.java)), the **Doctrine ORM** (see [`Note.php`](./src/test/php/Some/Note.php) and [`DoctrineTest.php`](./src/test/php/DoctrineTest.php)), the **Eloquent** (see [`NoteModel.php`](./src/test/php/Some/NoteModel.php) and [`EloquentTest.php`](./src/test/php/EloquentTest.php)), and, of course, it **can be integrated manually** into any project, giving you full flexibility to adapt it to your specific needs.

```
namespace PetrKnap\Persistence\ZonedDateTime;

$em = DoctrineTest::prepareEntityManager();

# persist entity
$em->persist(new Some\Note(
    createdAt: new \DateTimeImmutable('2025-10-30 23:52'),
    content: "It's dark outside...",
));
$em->flush();

# insert data manually (static call)
$now = new \DateTimeImmutable('2025-10-26 02:45', new \DateTimeZone('CEST'));
$em->getConnection()->insert('notes', [
    'created_at__utc' => ZonedDateTimePersistence::computeUtcDateTime($now)->format('Y-m-d H:i:s'),
    'created_at__local' => $now->format('Y-m-d H:i:s'),
    'content' => 'We still have summer time',
]);

# insert data manually (object instance)
$now = new UtcWithLocal(new \DateTimeImmutable('2025-10-26 02:15', new \DateTimeZone('CET')));
$em->getConnection()->insert('notes', [
    'created_at__utc' => $now->getUtcDateTime('Y-m-d H:i:s'),
    'created_at__local' => $now->getLocalDateTime('Y-m-d H:i:s'),
    'content' => 'Now we have winter time',
]);

# select entities
$notes = $em->createQueryBuilder()
    ->select('note')
    ->from(Some\Note::class, 'note')
    ->where('note.createdAt.local BETWEEN :from AND :to')
    ->orderBy('note.createdAt.utc')
    ->getQuery()
    ->execute(['from' => '2025-10-26 00:00', 'to' => '2025-10-26 23:59']);
foreach($notes as $note) {
    echo $note->getCreatedAt()->format('Y-m-d H:i T') . ': '. $note->getContent() . PHP_EOL;
}
```

```
2025-10-26 02:45 GMT+0200: We still have summer time
2025-10-26 02:15 GMT+0100: Now we have winter time

```

### UTC with timezone

[](#utc-with-timezone)

> `UtcWithTimezone`

If you want to **preserve the original timezone as is**, you cannot use [`UtcWithLocal`](#utc-with-local-date-time), because it works over fixed offsets. In this case, you need to use this implementation.

```
namespace PetrKnap\Persistence\ZonedDateTime;

$now = (new \DateTime('2025-03-30 01:45', new \DateTimeZone('Europe/Prague')));

echo 'UtcWithTimezone: ' . (new UtcWithTimezone($now))
    ->toZonedDateTime()
    ->modify('+1 hour')
    ->format('Y-m-d H:i T' . PHP_EOL);
echo 'UtcWithLocal:    ' . (new UtcWithLocal($now))
    ->toZonedDateTime()
    ->modify('+1 hour')
    ->format('Y-m-d H:i T' . PHP_EOL);
```

```
UtcWithTimezone: 2025-03-30 03:45 CEST
UtcWithLocal:    2025-03-30 02:45 GMT+0100

```

#### UTC with system timezone

[](#utc-with-system-timezone)

> `UtcWithSystemTimezone`

The **most compact** approach is to store **only the UTC date-time**. This serves as an alternative to MySQL's `TIMESTAMP`, Postgres's `TIMESTAMP WITH TIMEZONE`, and [custom ORM types](#utc-date-time-converter--type--cast). It offers full range of `DateTime`, avoids normalization on connection, adds `.utc` into your queries for better readability and didn't need special configuration.

#### UTC date-time converter / type / cast

[](#utc-date-time-converter--type--cast)

> `UtcDateTimeConverter` Jakarta Persistence API

This converter transparently manages conversions of `ZonedDateTime`, including JPQL parameters. That means you **no longer need to worry** about manual timezone adjustments.

For examples, see [the attributes `Note.createdAtUtc` and `Note.deletedAtUtc`](./src/test/java/some/Note.java) and [the `JpaTest`](./src/test/java/io/github/petrknap/persistence/zoneddatetime/JpaTest.java).

> `UtcDateTimeType` Doctrine ORM

In contrast to `UtcDateTimeConverter`, this type does **not** automatically adjust the timezone of DQL parameters. You must therefore **provide the type when you are calling `setParameter`** on your queries. Also, you have to **register the type** in your Doctrine configuration manually.

For examples, see [the attributes `Note.createdAtUtc` and `Note.deletedAtUtc`](./src/test/php/Some/Note.php) and [the `DoctrineTest`](./src/test/php/DoctrineTest.php).

> `AsUtcDateTime` Eloquent

In contrast to `UtcDateTimeConverter` and `UtcDateTimeType`, this cast may or may **not** adjust the timezone of any input. You should therefore **handle timezone conversions explicitly everytime you are providing date-time into Eloquent**. But the conversion after hydration works well.

For examples, see [the attributes `NoteModel.created_at_utc` and `NoteModel.deleted_at_utc`](./src/test/php/Some/NoteModel.php) and [the `EloquentTest`](./src/test/php/EloquentTest.php).

---

You can [support this project via donation](https://petrknap.github.io/donate.html). The project is licensed under [the terms of the `LGPL-3.0-or-later`](./COPYING.LESSER).

###  Health Score

41

—

FairBetter than 89% of packages

Maintenance78

Regular maintenance activity

Popularity20

Limited adoption so far

Community8

Small or concentrated contributor base

Maturity47

Maturing project, gaining track record

 Bus Factor1

Top contributor holds 100% of commits — single point of failure

How is this calculated?**Maintenance (25%)** — Last commit recency, latest release date, and issue-to-star ratio. Uses a 2-year decay window.

**Popularity (30%)** — Total and monthly downloads, GitHub stars, and forks. Logarithmic scaling prevents top-heavy scores.

**Community (15%)** — Contributors, dependents, forks, watchers, and maintainers. Measures real ecosystem engagement.

**Maturity (30%)** — Project age, version count, PHP version support, and release stability.

###  Release Activity

Cadence

Every ~5 days

Total

15

Last Release

118d ago

PHP version history (2 changes)v0.1.0PHP &gt;=8.1

0.2.2PHP &gt;=8.2

### Community

Maintainers

![](https://www.gravatar.com/avatar/c0e19f2ea17567eb9f536d1e00595b83630dfb8b2df6e7e422e06a9a48e85585?d=identicon)[petrknap](/maintainers/petrknap)

---

Top Contributors

[![petrknap](https://avatars.githubusercontent.com/u/8299754?v=4)](https://github.com/petrknap "petrknap (81 commits)")

---

Tags

datetimedoctrinedqleloquenthelperjava-libraryjpajpqllaravelorm-librarypersistencephp-librarysqlsummer-timetime-savingtimezonewinter-timezoneddatetimelaravelhelperpersistenceormdoctrinedqlsqleloquentdatetimetimezoneZonedDateTimetime-savingsummer-timewinter-time

###  Code Quality

TestsPHPUnit

Static AnalysisPHPStan

Code StylePHP\_CodeSniffer

Type Coverage Yes

### Embed Badge

![Health badge](/badges/petrknap-zoned-datetime-persistence/health.svg)

```
[![Health](https://phpackages.com/badges/petrknap-zoned-datetime-persistence/health.svg)](https://phpackages.com/packages/petrknap-zoned-datetime-persistence)
```

###  Alternatives

[illuminate/database

The Illuminate Database package.

2.8k52.4M9.4k](/packages/illuminate-database)[scienta/doctrine-json-functions

A set of extensions to Doctrine that add support for json query functions.

58723.9M36](/packages/scienta-doctrine-json-functions)[matchory/elasticsearch

The missing elasticsearch ORM for Laravel!

3059.0k](/packages/matchory-elasticsearch)

PHPackages © 2026

[Directory](/)[Categories](/categories)[Trending](/trending)[Changelog](/changelog)[Analyze](/analyze)
