feat: add timezone handling
This commit is contained in:
parent
349cee0ce6
commit
e8ec620d57
13 changed files with 128 additions and 28 deletions
|
|
@ -2,6 +2,7 @@ defmodule MvWeb.Helpers.DateFormatter do
|
|||
@moduledoc """
|
||||
Centralized date formatting helper for the application.
|
||||
Formats dates in European format (dd.mm.yyyy).
|
||||
DateTime can be shown in UTC or in a given IANA timezone (e.g. from browser).
|
||||
"""
|
||||
|
||||
use Gettext, backend: MvWeb.Gettext
|
||||
|
|
@ -28,19 +29,40 @@ defmodule MvWeb.Helpers.DateFormatter do
|
|||
@doc """
|
||||
Formats a DateTime struct to European format (dd.mm.yyyy HH:MM).
|
||||
|
||||
When `timezone` is a valid IANA timezone string (e.g. from the browser),
|
||||
the datetime is converted to that zone before formatting. When `timezone` is
|
||||
nil or invalid, the datetime is formatted in UTC.
|
||||
|
||||
## Examples
|
||||
|
||||
iex> MvWeb.Helpers.DateFormatter.format_datetime(~U[2024-03-15 10:30:00Z])
|
||||
"15.03.2024 10:30"
|
||||
|
||||
iex> MvWeb.Helpers.DateFormatter.format_datetime(~U[2024-03-15 10:30:00Z], "Europe/Berlin")
|
||||
"15.03.2024 11:30"
|
||||
|
||||
iex> MvWeb.Helpers.DateFormatter.format_datetime(nil)
|
||||
""
|
||||
"""
|
||||
def format_datetime(%DateTime{} = dt) do
|
||||
Calendar.strftime(dt, "%d.%m.%Y %H:%M")
|
||||
def format_datetime(%DateTime{} = dt), do: format_datetime(dt, nil)
|
||||
def format_datetime(nil), do: ""
|
||||
def format_datetime(_), do: "Invalid datetime"
|
||||
|
||||
def format_datetime(%DateTime{} = dt, nil), do: format_datetime_utc(dt)
|
||||
def format_datetime(%DateTime{} = dt, ""), do: format_datetime_utc(dt)
|
||||
|
||||
def format_datetime(%DateTime{} = dt, tz) when is_binary(tz) do
|
||||
case DateTime.shift_zone(dt, tz, Tz.TimeZoneDatabase) do
|
||||
{:ok, shifted} -> Calendar.strftime(shifted, "%d.%m.%Y %H:%M")
|
||||
{:error, _} -> format_datetime_utc(dt)
|
||||
end
|
||||
end
|
||||
|
||||
def format_datetime(nil), do: ""
|
||||
def format_datetime(nil, _timezone), do: ""
|
||||
|
||||
def format_datetime(_), do: "Invalid datetime"
|
||||
def format_datetime(_, _timezone), do: "Invalid datetime"
|
||||
|
||||
defp format_datetime_utc(%DateTime{} = dt) do
|
||||
Calendar.strftime(dt, "%d.%m.%Y %H:%M")
|
||||
end
|
||||
end
|
||||
|
|
|
|||
|
|
@ -63,7 +63,7 @@ defmodule MvWeb.JoinRequestLive.Index do
|
|||
>
|
||||
<:col :let={req} label={gettext("Submitted at")}>
|
||||
<%= if req.submitted_at do %>
|
||||
{DateFormatter.format_datetime(req.submitted_at)}
|
||||
{DateFormatter.format_datetime(req.submitted_at, @browser_timezone)}
|
||||
<% else %>
|
||||
<.empty_cell sr_text={gettext("Not submitted yet")} />
|
||||
<% end %>
|
||||
|
|
@ -125,7 +125,7 @@ defmodule MvWeb.JoinRequestLive.Index do
|
|||
</.badge>
|
||||
</:col>
|
||||
<:col :let={req} label={gettext("Reviewed at")}>
|
||||
{review_date(req)}
|
||||
{review_date(req, @browser_timezone)}
|
||||
</:col>
|
||||
<:col :let={req} label={gettext("Review by")}>
|
||||
{JoinRequestHelpers.reviewer_display(req) || ""}
|
||||
|
|
@ -162,7 +162,7 @@ defmodule MvWeb.JoinRequestLive.Index do
|
|||
assign(socket, :page_title, gettext("Join requests"))
|
||||
end
|
||||
|
||||
defp review_date(req) do
|
||||
defp review_date(req, timezone) do
|
||||
date =
|
||||
case req.status do
|
||||
:approved -> req.approved_at
|
||||
|
|
@ -170,6 +170,6 @@ defmodule MvWeb.JoinRequestLive.Index do
|
|||
_ -> nil
|
||||
end
|
||||
|
||||
if date, do: DateFormatter.format_datetime(date), else: ""
|
||||
if date, do: DateFormatter.format_datetime(date, timezone), else: ""
|
||||
end
|
||||
end
|
||||
|
|
|
|||
|
|
@ -144,7 +144,7 @@ defmodule MvWeb.JoinRequestLive.Show do
|
|||
<div class="border border-base-300 rounded-lg p-4 bg-base-100 space-y-2">
|
||||
<.field_row
|
||||
label={gettext("Submitted at")}
|
||||
value={DateFormatter.format_datetime(@join_request.submitted_at)}
|
||||
value={DateFormatter.format_datetime(@join_request.submitted_at, @browser_timezone)}
|
||||
/>
|
||||
<div class="flex gap-2">
|
||||
<span class="text-base-content/60 min-w-32 shrink-0">{gettext("Status")}:</span>
|
||||
|
|
@ -158,13 +158,17 @@ defmodule MvWeb.JoinRequestLive.Show do
|
|||
<%= if @join_request.approved_at do %>
|
||||
<.field_row
|
||||
label={gettext("Approved at")}
|
||||
value={DateFormatter.format_datetime(@join_request.approved_at)}
|
||||
value={
|
||||
DateFormatter.format_datetime(@join_request.approved_at, @browser_timezone)
|
||||
}
|
||||
/>
|
||||
<% end %>
|
||||
<%= if @join_request.rejected_at do %>
|
||||
<.field_row
|
||||
label={gettext("Rejected at")}
|
||||
value={DateFormatter.format_datetime(@join_request.rejected_at)}
|
||||
value={
|
||||
DateFormatter.format_datetime(@join_request.rejected_at, @browser_timezone)
|
||||
}
|
||||
/>
|
||||
<% end %>
|
||||
<.field_row
|
||||
|
|
|
|||
|
|
@ -22,6 +22,15 @@ defmodule MvWeb.LiveHelpers do
|
|||
def on_mount(:default, _params, session, socket) do
|
||||
locale = session["locale"] || "de"
|
||||
Gettext.put_locale(locale)
|
||||
|
||||
# Browser timezone from LiveSocket connect params (set in app.js via Intl API)
|
||||
connect_params = socket.private[:connect_params] || %{}
|
||||
timezone = connect_params["timezone"] || connect_params[:timezone]
|
||||
|
||||
socket =
|
||||
socket
|
||||
|> assign(:browser_timezone, timezone)
|
||||
|
||||
{:cont, socket}
|
||||
end
|
||||
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue