defmodule QuackDB.NanosecondTimestamp do @moduledoc """ DuckDB `TIMESTAMP_NS` value stored as nanoseconds since the Unix epoch. Elixir's `NaiveDateTime` type stores microseconds, so this struct preserves DuckDB's nanosecond precision while offering conversion to a truncated `NaiveDateTime`. """ defstruct [:nanoseconds] @type t :: %__MODULE__{nanoseconds: integer()} @doc "Builds a `TIMESTAMP_NS` value from nanoseconds since the Unix epoch." @spec new(integer()) :: t() def new(nanoseconds) when is_integer(nanoseconds) do %__MODULE__{nanoseconds: nanoseconds} end @doc "Builds a `TIMESTAMP_NS` value from an Elixir `NaiveDateTime`." @spec from_naive_datetime(NaiveDateTime.t()) :: t() def from_naive_datetime(%NaiveDateTime{} = value) do new(NaiveDateTime.diff(value, ~N[1970-01-01 00:00:00], :nanosecond)) end @doc "Converts to an Elixir `NaiveDateTime`, truncating sub-microsecond precision." @spec to_naive_datetime(t()) :: NaiveDateTime.t() def to_naive_datetime(%__MODULE__{nanoseconds: nanoseconds}) do NaiveDateTime.add(~N[1970-01-01 00:00:00], div(nanoseconds, 1_000), :microsecond) end @doc "Returns the stored nanoseconds since the Unix epoch." @spec to_integer(t()) :: integer() def to_integer(%__MODULE__{nanoseconds: nanoseconds}), do: nanoseconds end if Code.ensure_loaded?(Inspect) do defimpl Inspect, for: QuackDB.NanosecondTimestamp do import Inspect.Algebra def inspect(value, opts) do concat([ string("#QuackDB.NanosecondTimestamp<"), to_doc(value.nanoseconds, opts), string(" ns>") ]) end end end