I’ve been working on an interesting problem to solve in Laravel lately for a private monitoring dashboard application that I am building. One of the requirement is to display the date either as a text, or the value of a datepicker input field according to the current timezone.
In this article, I would like to walk through the final decision that I made, and the technical implementation around it.
Essentially what I ended up with are three key points:
- Always stored the date in UTC.
- Create a custom cast to handle the conversion.
- Let the user decides on the timezone themselves.
Always stored the date in UTC
Your implementation may vary, but I think for maximum compatibility and less headache down the road, always stored the date in UTC
in the database. That way, you do not have to waste your time to figure out the timezone of the date when you’re fetching it from the database.
Which leads me to the point #2.
Create a custom cast to handle the conversion
On top of various built-in casts provided by Laravel out of the box, it also has support for custom defined cast as mentioned here. Ultimately, all you need to care about are:
get
method to handle conversion of the date from UTC to the user-defined timezone.set
method to handle conversion of the user input back to UTC before it is saved into the database again.
To get started, we can create a custom cast for our model attribute via:
|
|
The entirety of the LocalizedDatetime
cast is as follows:
|
|
To apply this custom cast to our Eloquent model attribute, we can simply configure them inside the $casts
property. Assuming we have an imaginary model called Post
that has an attribute called published_at
to store the published date, then our Post
$casts
property will look like this.
|
|
Once this is configured, anytime we try to access the published_at
property, it will be automatically converted to the date with the correct user-defined timezone.
However, there are a few things I would like to note from the above implementation:
- I am using
CarbonImmutable
here, which is outside the scope of this article, but if you are interested, I recommend reading about this ever excellent post by Michael Dyrynda on using immutable dates in Laravel. config('app.timezone')
isUTC
by default, and I do not recommend changing them.get_timezone
is a simple helper to fetch the user-defined timezone that fallback toUTC
which I’ll expand on in the next section.
Let the user decides on the timezone themselves
As mentioned above, get_timezone
is simply a helper function to retrieve the timezone defined by the user.
|
|
Again, if it’s not defined, or there’s no authenticated user, then it will fall back to UTC
.
I’ve seen few other alternatives implementation to fetch the user timezone, ranging from deducing the timezone from the user’s IP address on login, to detecting it via JavaScript then send it to the server.
Personally I think what works best in my use case is to define a new column called timezone
that is nullable and allows user to select it manually from a dropdown. A nullable column also helps me to define custom logic that will allow me to display certain notice on the dashboard in case it hasn’t been set yet.