date_timezone_set()
Learn how PHP's date_timezone_set() converts a DateTime object to another timezone without changing the instant. Syntax, DST handling, and examples.
Introduction
date_timezone_set() changes the timezone of an existing DateTime object. Crucially, it does not change the underlying instant in time — it changes the wall-clock representation of that instant. The same moment is simply re-expressed in a different timezone, so the displayed hours and minutes shift while the absolute point on the timeline stays identical.
This makes it the right tool whenever you have a date/time in one zone and need to show it in another: converting a stored UTC timestamp into a user's local time, or comparing events recorded across different regions.
date_timezone_set() is the procedural alias of the DateTime::setTimezone() method — both do exactly the same thing.
Syntax
date_timezone_set(DateTime $object, DateTimeZone $timezone): DateTime|false$object— theDateTimeobject whose timezone you want to change.$timezone— aDateTimeZoneobject holding the target zone (for examplenew DateTimeZone('Asia/Tokyo')).
The function mutates $object in place and returns the same instance on success (handy for chaining), or false on failure. Because it modifies the original object, there is no separate "copy" — the input object itself changes.
Basic example
2023-03-03 21:00:00The object starts at 12:00 in London (Europe/London, which is UTC+0 in March). Tokyo is 9 hours ahead, so the same instant is displayed as 21:00. Nothing about the actual moment changed — only the zone it is shown in.
The instant stays the same — only the display shifts
This is the single most important thing to understand about date_timezone_set(). Compare the value before and after the conversion:
<?php
$date = new DateTime('2023-03-03 12:00:00', new DateTimeZone('Europe/London'));
echo $date->format('Y-m-d H:i:s P'), "\n"; // before
date_timezone_set($date, new DateTimeZone('Asia/Tokyo'));
echo $date->format('Y-m-d H:i:s P'), "\n"; // after2023-03-03 12:00:00 +00:00
2023-03-03 21:00:00 +09:00The wall-clock time and the UTC offset both change, but 12:00 +00:00 and 21:00 +09:00 are the same point in time. If you only need the absolute instant (for example, a UNIX timestamp), it will be unchanged.
If you instead want to replace the wall-clock time without converting it, do not use this function — set the time directly with
date_time_set().
Daylight saving time is handled for you
Because the conversion goes through a real DateTimeZone, daylight saving (DST) rules are applied automatically. New York is UTC-5 in winter but UTC-4 in summer, and date_timezone_set() picks the correct offset for the date in question:
<?php
$utc = new DateTime('2023-06-21 12:00:00', new DateTimeZone('UTC'));
date_timezone_set($utc, new DateTimeZone('America/New_York'));
echo $utc->format('Y-m-d H:i:s');2023-06-21 08:00:00In June, New York is on Eastern Daylight Time (UTC-4), so 12:00 UTC becomes 08:00. Run the same code with a January date and you would get 07:00 (UTC-5) instead — without any change to your code. This is why you should always use IANA zone identifiers like America/New_York rather than fixed offsets like -05:00.
Procedural vs. object-oriented style
date_timezone_set($obj, $tz) and $obj->setTimezone($tz) are interchangeable. The method form usually reads better and chains naturally:
<?php
$london = (new DateTime('2023-03-03 12:00:00', new DateTimeZone('Europe/London')))
->setTimezone(new DateTimeZone('Asia/Tokyo'));
echo $london->format('Y-m-d H:i:s');2023-03-03 21:00:00Common use cases
- Display stored UTC times in a user's zone. Store every timestamp in UTC, then convert on output with
date_timezone_set()for each user's preferred timezone. - Normalize times from multiple regions before comparing or sorting them.
- Build region-aware reports, showing the same event in several zones side by side.
Things to watch out for
- It mutates the original object. If you need to keep the source value, clone it first:
$copy = clone $date;then convert$copy. - It does not parse or set a new date — it only re-projects the existing instant. Use
date_create()to build aDateTimeanddate_time_set()to overwrite the time. - To read back the zone currently attached to an object, use
date_timezone_get(). - Don't confuse it with
date_default_timezone_set(), which sets the script-wide default zone for all date/time functions — a completely different job.
Diagram
graph LR
A[DateTime Object] --> B((date_timezone_set))
C[Target DateTimeZone] --> B
B --> D[Same instant, new wall-clock display]