Working with UTC timestamps on server but local datetimes on client side with JavaScript for conversion

What I want is to work with UTC timestamps on the server side, ignoring time zone and locale information. Now, as a client is at a specific time zone and locale, I want to convert/format my UTC timestamps accordingly. I can do that simply with LiveView’s hooks. So, e.g. when I want to display a timestamp, I can add a hook to it and convert/format it with JavaScript on the mounted event. I can also update it on the updated event.

However, with datetime_local_input fields, it’s getting more complicated. When I create such a field, I could default it to the current UTC time. This value can be converted on the client side with JavaScript, just like in my previous example. Now, after the user inputs a local datetime, how do I convert it back to a UTC timestamp? I could add a listener (onsubmit?) to the field via hook and convert the value before it is submitted. But after that, I would need to convert it back; otherwise, the user would see the UTC datetime. I don’t know how to do that. Another problem is, what when I’m using phx_change? Every time the user changes the datetime field, I’d need to do a conversion. But as I’ve shown, simply converting the value on client side on every change doesn’t work.

I hope that it kinda gets clear what I’m struggling with. In a perfect world, I’d like to have a way to simply add some JavaScript to an element, field or whatever. Then, every time the server communicates with the client and vice versa, I’d do a conversion of the data. So basically, the server and client each would get their own representation of the same information, without me having to mess around with events, listeners, hooks or whatever. Is there an idiomatic way to do this with LiveView?

1 Like

I store client side timezone in a liveview assign and convert everything server side.


I’m with @derek-zhou, I would avoid implicit timezone conversation always. Instead, a user can configure under their profile or settings what timezone they want to see things in. Then, any time you display a time, you convert the UTC time to their preferred timezone. Any time you get an input, you convert from their timezone back to UTC.

In addition to being nice and explicit, it also allows end users to view data in any time zone they want, even if they are not currently located there. This can be very helpful when trying to coordinate with someone else not in your current timezone.


Currently, I’m also using just an assign for the time zone offset in sockets. It works for my case but for other cases, one would need to use something like Timex for extra functionality. Also, I’m thinking about how to add localization to my app and if I would go the server-side approach, I’d need another dependency like Cldr. I ask myself, shouldn’t the browser/JavsScript be responsible for these things? In the end, it’s the client-side that is the “view” of the data, or “model”, managed by my server. The client-side should be responsible for conversion and formatting of a UTC timestamp to a human-readable local datetime. JavaScript offers – as far as I can tell – all the needed tools natively. I feel like using assigns for the time zone, locale and so on is inherently the wrong thing to do here and we do it, because there is no better way to do it in LiveView. I could be wrong though; I don’t know … That’s why I wrote the post.

The value proposition of liveview is to put as much logic at the server side as possible. It is not the only way to do things, but it is the easiest to reason about, to explore, and to test.

Since you brought up localization, I am curious: how do you do localization at the client side? You translate all text at the client side?

I’m focusing on timestamps/datetimes in my post. E.g.

let d = new Date("2022-04-15T00:00:00Z")

prints the given UTC timestamp in your browser’s locale and time zone.

I think, general localization is indeed a server-side thing. I haven’t worked with Gettext yet but it seems to be the built-in tool for that in LiveView.


I usually use a javascript custom component for localizing numbers and date/time formats. Like:

class MyLocalDatetime extends HTMLElement {
  static get observedAttributes () {
    return ['iso-datetime']

  attributeChangedCallback (name, oldValue, newValue) {
      'iso-datetime': (newValue) => {
        this.innerHTML = new Date(newValue).toLocaleString()

window.customElements.define('my-local-datetime', MyLocalDatetime)

Combined with a phoenix component like this:

defmodule MyAppWeb.Components.CustomElements do
  use MyAppWeb, :component

  def local_datetime(assigns) do
    <my-local-datetime {assigns} iso-datetime={@datetime} phx-update="ignore"><%= @datetime %></my-local-datetime>

Works great so far for me. And the usage is quite easy:

<.local_datetime datetime={@tour.atm}/>

… where tour.atm is a DateTime struct.