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