My users are in India, US and UK. Dates saved from date('Y-m-d H:i:s') are off by hours depending on server, and adding 1 month to Jan 31 gives March 3. How do I do it properly?
DEV3 Developer Forum
How should I handle dates and timezones in PHP correctly?
Every post is reviewed first. Usually reviewed within 24 hours.
1 reply
Treat time as UTC in storage and convert to the user's zone only for display.
1. Set a default and store UTC
date_default_timezone_set('UTC'); // or default_timezone in php.ini: date.timezone = UTC
$now = new DateTimeImmutable('now', new DateTimeZone('UTC'));
$db->execute([$now->format('Y-m-d H:i:s')]);
Set the MySQL connection to UTC as well (SET time_zone = '+00:00') if you use NOW().
2. Convert for display
$utc = new DateTimeImmutable($row['created_at'], new DateTimeZone('UTC'));
$local = $utc->setTimezone(new DateTimeZone($user->timezone)); // e.g. 'Asia/Kolkata'
echo $local->format('d M Y, H:i T');
Store the IANA timezone name (America/New_York), not an offset like -05:00, because offsets change with daylight saving time.
3. Prefer DateTimeImmutable
DateTime is mutable: $d->modify('+1 day') changes the original, which causes surprising bugs when you reuse the object. DateTimeImmutable returns a new instance.
$start = new DateTimeImmutable('2026-01-31');
$next = $start->modify('last day of next month'); // 2026-02-28
The "Jan 31 + 1 month = Mar 3" behaviour is PHP overflowing the day. Use last day of next month or Carbon::addMonthNoOverflow() in Laravel.
4. Differences and periods
$diff = $a->diff($b); // DateInterval
echo $diff->days; // total days
$period = new DatePeriod($start, new DateInterval('P1D'), $end);
Pitfalls
strtotime()anddate()use the default zone and are limited to specific formats; ambiguous strings like03/04/2026are parsed as USm/d/Y. Parse withDateTimeImmutable::createFromFormat('d/m/Y', $s).- Check
createFromFormatresult withDateTimeImmutable::getLastErrors(). - Do not calculate 24 hours as a day across DST changes; use calendar modifiers.
- Timestamps (seconds since 1970) are always UTC.
Handy tools: timestamp converter and time zone converter.