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.
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');
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
| Region | Identifier |
|---|---|
| United Kingdom | Europe/London |
| US Eastern | America/New_York |
| US Pacific | America/Los_Angeles |
| Central Europe | Europe/Berlin, Europe/Paris |
| Australia Eastern | Australia/Sydney |
| No local adjustment wanted | UTC |
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.
A related warning worth recognising
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
Cron runs a PHP script on a schedule with no browser involved — here is the five-field syntax and how to check a run actually worked.
How to read a PHP error logThe error log names the exact file and line that failed — here is where to find it and how to read what it is telling you.
How to check which PHP version your site usesA one-line PHP script and the PHP settings screen in your control panel both show the exact version your site runs on.
How to call an external API from PHPcURL, an authentication header and a decoded JSON response — the three parts of calling any external API from a PHP script.