defmodule ExIhdlUnits.Zse.DateTime do @behaviour Ecto.Type @seconds_per_year 31_557_600 @zse_ts_epoch_offset_years 30 alias Elixir.DateTime, as: ElixirDateTime def type, do: :utc_datetime def cast(device_recorded_at_zsets) when is_integer(device_recorded_at_zsets) do with {:ok, device_recorded_at_zse_utc} <- ElixirDateTime.from_unix(device_recorded_at_zsets) do cast(device_recorded_at_zse_utc) end end def cast(device_recorded_at_zsets) when is_binary(device_recorded_at_zsets) do with {:ok, device_recorded_at_zse_utc, _utc_offset} <- ElixirDateTime.from_iso8601(device_recorded_at_zsets) do cast(device_recorded_at_zse_utc) end end def cast(%ElixirDateTime{} = device_recorded_at_zse_utc) do years_diff = ElixirDateTime.diff( ElixirDateTime.utc_now(), device_recorded_at_zse_utc, :second ) device_recorded_at = cond do years_diff > 20 * @seconds_per_year -> Timex.shift(device_recorded_at_zse_utc, years: @zse_ts_epoch_offset_years) true -> device_recorded_at_zse_utc end {:ok, device_recorded_at |> ElixirDateTime.truncate(:second)} end # Everything else is a failure though def cast(_), do: :error # When loading data from the database, we are guaranteed to # receive a map (as databases are strict) and we will # just put the data back into an URI struct to be stored # in the loaded schema struct. def load(data) do {:ok, ElixirDateTime.from_naive!(data, "Etc/UTC") |> ElixirDateTime.truncate(:second)} end # When dumping data to the database, we *expect* an URI struct # but any value could be inserted into the schema struct at runtime, # so we need to guard against them. def dump(%ElixirDateTime{} = datetime), do: {:ok, datetime |> ElixirDateTime.truncate(:second)} def dump(_), do: :error end