Current section

Files

Jump to
postgrex lib postgrex extensions timetz.ex
Raw

lib/postgrex/extensions/timetz.ex

defmodule Postgrex.Extensions.TimeTZ do
@moduledoc false
import Postgrex.BinaryUtils, warn: false
use Postgrex.BinaryExtension, [send: "timetz_send"]
@day (:calendar.time_to_seconds({23, 59, 59}) + 1) * 1_000_000
def init(opts), do: Keyword.fetch!(opts, :date)
def encode(:postgrex) do
quote location: :keep do
%Postgrex.Time{} = time ->
unquote(__MODULE__).encode_postgrex(time)
other ->
raise ArgumentError, Postgrex.Utils.encode_msg(other, Postgrex.Time)
end
end
def encode(:elixir) do
quote location: :keep do
%Time{} = time ->
unquote(__MODULE__).encode_elixir(time)
other ->
raise ArgumentError, Postgrex.Utils.encode_msg(other, Time)
end
end
def decode(:postgrex) do
quote location: :keep do
<<12 :: int32, microsecs :: int64, tz :: int32>> ->
unquote(__MODULE__).microsecond_to_postgrex(microsecs, tz)
end
end
def decode(:elixir) do
quote location: :keep do
<<12 :: int32, microsecs :: int64, tz :: int32>> ->
unquote(__MODULE__).microsecond_to_elixir(microsecs, tz)
end
end
## Helpers
def encode_postgrex(%Postgrex.Time{hour: hour, min: min, sec: sec, usec: usec})
when hour in 0..23 and min in 0..59 and sec in 0..59 and usec in 0..999_999 do
time = {hour, min, sec}
<<12 :: int32, :calendar.time_to_seconds(time) * 1_000_000 + usec :: int64,
0 :: int32>>
end
def microsecond_to_postgrex(microsec, tz) do
microsec
|> adjust_microsecond(tz)
|> microsecond_to_postgrex()
end
defp adjust_microsecond(microsec, tz) do
case microsec + tz * 1_000_000 do
adjusted_microsec when adjusted_microsec < 0 ->
@day + adjusted_microsec
adjusted_microsec when adjusted_microsec < @day ->
adjusted_microsec
adjusted_microsec ->
adjusted_microsec - @day
end
end
defp microsecond_to_postgrex(microsecs) do
secs = div(microsecs, 1_000_000)
usec = rem(microsecs, 1_000_000)
{hour, min, sec} = :calendar.seconds_to_time(secs)
%Postgrex.Time{hour: hour, min: min, sec: sec, usec: usec}
end
def encode_elixir(%Time{hour: hour, minute: min, second: sec, microsecond: {usec, _}})
when hour in 0..23 and min in 0..59 and sec in 0..59 and usec in 0..999_999 do
time = {hour, min, sec}
<<12 :: int32, :calendar.time_to_seconds(time) * 1_000_000 + usec :: int64, 0 :: int32>>
end
def microsecond_to_elixir(microsec, tz) do
microsec
|> adjust_microsecond(tz)
|> microsecond_to_elixir()
end
defp microsecond_to_elixir(microsec) do
sec = div(microsec, 1_000_000)
microsec = rem(microsec, 1_000_000)
sec
|> :calendar.seconds_to_time()
|> Time.from_erl!({microsec, 6})
end
end