Packages

Behaviour and use macro for Raven display integrations

Current section

Files

Jump to
raven_observer_display_sdk lib code_name_raven timezone.ex
Raw

lib/code_name_raven/timezone.ex

defmodule CodeNameRaven.Timezone do
@moduledoc """
A robust, enterprise-grade timezone utility for Code Name Raven.
Utilizes the official IANA Time Zone Database (Tzdb) via the `tz` package
to natively, safely, and dynamically shift UTC DateTime structs to any
target timezone (such as `"America/Chicago"`) on-the-fly, handling all
Daylight Saving Time transitions flawlessly.
"""
@doc """
Formats a UTC DateTime based on the display timezone requested by the integration module.
Defaults to UTC if no timezone is supplied or supported.
"""
@spec format_datetime(DateTime.t() | nil, module() | nil) :: String.t()
def format_datetime(nil, _mod), do: "Waiting for first check…"
def format_datetime(%DateTime{} = dt, mod) do
tz = if mod && function_exported?(mod, :display_timezone, 0), do: mod.display_timezone(), else: "UTC"
case shift_timezone(dt, tz) do
{:ok, shifted, suffix} ->
Calendar.strftime(shifted, "%I:%M:%S %p ") <> suffix
_ ->
Calendar.strftime(dt, "%I:%M:%S %p UTC")
end
end
@doc """
Shifts a UTC DateTime and returns it as a fake UTC ISO-8601 string
representing the target timezone, so that client-side Plotly.js
can parse it as a native Date and apply downsampling.
"""
@spec format_chart_time(DateTime.t(), module() | nil) :: String.t()
def format_chart_time(%DateTime{} = dt, mod) do
tz = if mod && function_exported?(mod, :display_timezone, 0), do: mod.display_timezone(), else: "UTC"
case shift_timezone(dt, tz) do
{:ok, shifted, _suffix} ->
# Convert the shifted date back into a fake UTC ISO-8601 string so Plotly parses it as a native Date.
DateTime.to_iso8601(shifted)
_ ->
DateTime.to_iso8601(dt)
end
end
@doc """
Natively shifts a UTC DateTime to the target IANA timezone.
Returns `{:ok, shifted_datetime, zone_abbreviation}`.
"""
@spec shift_timezone(DateTime.t(), String.t()) :: {:ok, DateTime.t(), String.t()} | {:error, any()}
def shift_timezone(%DateTime{} = dt, "UTC"), do: {:ok, dt, "UTC"}
def shift_timezone(%DateTime{} = dt, tz) do
case DateTime.shift_zone(dt, tz) do
{:ok, shifted} ->
{:ok, shifted, shifted.zone_abbr}
{:error, reason} ->
{:error, reason}
end
end
end