Files

53 lines
1.6 KiB
Elixir
Raw Permalink Normal View History

2026-07-04 19:27:17 +09:00
defmodule Rsh26pool.JST do
@moduledoc """
Helpers for converting between UTC (how timestamps are stored) and
Japan Standard Time (JST, UTC+9) which is what the UI shows and accepts.
A full time zone database is not configured for this demo, and JST has no
daylight saving time, so a fixed +9h offset is sufficient and dependency free.
"""
@offset_seconds 9 * 3600
@doc """
Parses a `datetime-local` form value (e.g. `"2026-07-04T15:30"`), interpreting
it as wall-clock JST, and returns the corresponding UTC `DateTime`.
"""
@spec parse_local(String.t()) :: {:ok, DateTime.t()} | :error
def parse_local(value) when is_binary(value) do
with {:ok, naive} <- NaiveDateTime.from_iso8601(ensure_seconds(value)) do
utc =
naive
|> DateTime.from_naive!("Etc/UTC")
|> DateTime.add(-@offset_seconds, :second)
|> DateTime.truncate(:second)
{:ok, utc}
else
_ -> :error
end
end
def parse_local(_), do: :error
@doc "Converts a stored UTC `DateTime` to a JST `DateTime` for display."
@spec to_local(DateTime.t()) :: DateTime.t()
def to_local(%DateTime{} = dt), do: DateTime.add(dt, @offset_seconds, :second)
@doc "Formats a stored UTC `DateTime` as a human friendly JST string."
@spec format(DateTime.t() | nil) :: String.t()
def format(nil), do: ""
def format(%DateTime{} = dt) do
l = to_local(dt)
"~4..0B/~2..0B/~2..0B ~2..0B:~2..0B"
|> :io_lib.format([l.year, l.month, l.day, l.hour, l.minute])
|> to_string()
end
defp ensure_seconds(value) do
if Regex.match?(~r/T\d{2}:\d{2}$/, value), do: value <> ":00", else: value
end
end