Guide PHP & Development

How to get PHP dates and timezones right

Setting the right timezone in php.ini, and the one habit — storing everything in UTC — that stops multi-timezone dates going wrong.

Updated 6 min read Intermediate

PHP has to be told what timezone to assume, because nothing about a server tells it this on its own with any reliability. Left unset, PHP falls back to a default — often UTC — that has nothing to do with where your visitors, your business, or you personally are actually located, and every date function in the script then quietly uses the wrong one.

Setting it properly, at the server level

The reliable fix is the date.timezone directive, set once through the PHP settings area of your control panel or a user-level php.ini, so every script on the site inherits the correct value without needing to set it individually:

date.timezone = Europe/London

Use a full region/city identifier from the IANA timezone database — Europe/London, America/New_York, Australia/Sydney — rather than an abbreviation like GMT or EST. Abbreviations are ambiguous and several of them do not correctly account for daylight saving changes; region/city identifiers do, automatically, without any extra code.

Setting it inside a script instead

If you cannot change the server-level setting, or a specific script genuinely needs a different timezone to the rest of the site, set it at the top of that script before any date functions run:

<?php
date_default_timezone_set('Europe/London');
Without either of these, PHP will not error — it will just be wrong

An unset timezone does not throw a visible failure; every date and time calculation simply runs against the wrong assumption silently. This is what makes timezone bugs so persistent — nothing points at the problem directly, and it often surfaces first as "why is this timestamp three hours off" rather than as an error message pointing at a cause.

Common identifiers

RegionIdentifier
United KingdomEurope/London
US EasternAmerica/New_York
US PacificAmerica/Los_Angeles
Central EuropeEurope/Berlin, Europe/Paris
Australia EasternAustralia/Sydney
No local adjustment wantedUTC

PHP maintains the full IANA list internally, so any valid region/city pair works — these are simply the ones asked about most often.

Confirming what is actually set

<?php
echo date_default_timezone_get();
echo date('Y-m-d H:i:s');

Compare the second line against the actual current time. A mismatch confirms the timezone setting, not the code around it, is the thing to fix.

The one habit that prevents most multi-timezone bugs: store in UTC

Any application with users in more than one timezone — which, for a public website, is effectively all of them — should store every timestamp in the database as UTC, and convert to a local timezone only at the point of display. Storing local time directly in the database is the single most common cause of timezone bugs, because a stored local time has no record of which timezone it was local to, making it ambiguous the moment more than one timezone is involved anywhere in the system.

<?php
$utcNow = new DateTime('now', new DateTimeZone('UTC'));
echo $utcNow->format('Y-m-d H:i:s');   // store this

$local = clone $utcNow;
$local->setTimezone(new DateTimeZone('Europe/London'));
echo $local->format('Y-m-d H:i:s');    // display this

The DateTime and DateTimeZone classes handle the conversion correctly, including daylight saving transitions, which is exactly the kind of detail worth letting the language handle rather than reimplementing with manual hour arithmetic.

Timezones and cron

A cron schedule runs according to the server's own system timezone, which is not necessarily the same as the PHP-level date.timezone setting used inside a script. If a scheduled script is firing at an unexpected time, check the server's system timezone rather than assuming the PHP setting above is what controls it — the two are configured separately and can legitimately differ.

MySQL has its own timezone setting too

If dates are being compared or converted at the database level with functions like CONVERT_TZ(), MySQL's own timezone tables and session timezone come into play independently of anything set in PHP. For most applications the simplest and most robust approach avoids this entirely — store UTC as plain values and do all timezone conversion in PHP at display time, rather than splitting the responsibility between the two layers.

Older PHP versions emit "It is not safe to rely on the system's timezone settings" when date.timezone is unset and a date function is called. If you ever see that message in a log or on screen, it is describing exactly the problem this article covers — set date.timezone at the server level and the warning stops appearing on every single date call across the site rather than needing to be silenced call by call.

Related reading