Users can set their timezone and we store it in the database. Then we can dynamically change the system timezone on each page load in Middleware or in our AppServiceProvider.
Example: https://stackoverflow.com/a/47938555/5156146
The problem with this is that it messes up our logs. Any logs will be in the user's local timezone instead of UTC.
How can we set the timezone dynamically for each user while still keeping the timezone correct in our log files?
Currently we use date()
and now()
in many places throughout my code. I want these to all be set to the user's timezone but still have any logs stored in UTC.
Changing PHP's default timezone will alter the timezone used for the logs. To avoid that, don't change the default timezone. You can even block the default timezone functions from being used though this might cause errors if you have any code/libraries that uses them. There are various methods to get formatted date & time without changing PHP's default timezone, using the date()
and now()
functions are not among them.
Using pure PHP, you can use the DateTime
and DateTimeZone
objects:
$userTz = new DateTimeZone($userTimezoneString);
$userNow = new DateTime('now', $userTz);
echo $userNow->format('Y-m-d H:i:s'); // 2023-05-09 12:22:46
There are some date/time libraries available that provide additional options for managing dates. A popular one is the Carbon
library (needs to be installed first; for instance, using composer
). In fact, Carbon extends off of and adds additional methods and functionality to PHP's DateTime
and other related date/time/timezone classes:
$userNow = Carbon::now($userTimezoneString);
$userNow->toDateTimeString(); // 2023-05-09 12:22:46
A third, and in my opinion the most powerful option, is to use the ICU INTL
extension (short for Internationalization; the extension needs to be activated in the PHP.ini file but comes bundled with PHP by default). This library is fast and powerful. It used to be disabled by most web hosting early on but has matured and functions in modern PHP are even being deprecated and removed in favor of their INTL
equivalents. Most web hosts now have this extension enabled as it's used by an increasing number of libraries for translation, tokenization, transliteration, formatting, etc.
$now = new DateTime(); // No timezone required at this step
$userDateFormatter = new IntlDateFormatter(
$userLocale, // User's locale, such as en_US, en_UK, de_DE, ar_EG, etc.
IntlDateFormatter::FULL,
IntlDateFormatter::FULL,
$userTimezoneString, // User's timezone
IntlDateFormatter::GREGORIAN
);
echo $userDateFormatter->format($now);
// Dienstag, 9. Mai 2023 um 14:22:46 Mitteleuropäische Sommerzeit
// (Assuming de_DE user locale and Europe/Berlin user timezone)
You saw that correctly, it will even translate to the user's language as well as use the date/time format commonly expected by users of the region (using the locale variable) and convert the given DateTime
object's information to the given timezone when formatting... all without touching the default timezone. You can use any of the supported locales.