timetz.ex (1637B)
1 defmodule Postgrex.Extensions.TimeTZ do 2 @moduledoc false 3 import Postgrex.BinaryUtils, warn: false 4 use Postgrex.BinaryExtension, send: "timetz_send" 5 6 @day (:calendar.time_to_seconds({23, 59, 59}) + 1) * 1_000_000 7 8 def encode(_) do 9 quote location: :keep do 10 %Time{calendar: Calendar.ISO} = time -> 11 unquote(__MODULE__).encode_elixir(time) 12 13 other -> 14 raise DBConnection.EncodeError, Postgrex.Utils.encode_msg(other, Time) 15 end 16 end 17 18 def decode(_) do 19 quote location: :keep do 20 <<12::int32(), microsecs::int64(), tz::int32()>> -> 21 unquote(__MODULE__).microsecond_to_elixir(microsecs, tz) 22 end 23 end 24 25 ## Helpers 26 27 defp adjust_microsecond(microsec, tz) do 28 case microsec + tz * 1_000_000 do 29 adjusted_microsec when adjusted_microsec < 0 -> 30 @day + adjusted_microsec 31 32 adjusted_microsec when adjusted_microsec < @day -> 33 adjusted_microsec 34 35 adjusted_microsec -> 36 adjusted_microsec - @day 37 end 38 end 39 40 def encode_elixir(%Time{hour: hour, minute: min, second: sec, microsecond: {usec, _}}) 41 when hour in 0..23 and min in 0..59 and sec in 0..59 and usec in 0..999_999 do 42 time = {hour, min, sec} 43 <<12::int32(), :calendar.time_to_seconds(time) * 1_000_000 + usec::int64(), 0::int32()>> 44 end 45 46 def microsecond_to_elixir(microsec, tz) do 47 microsec 48 |> adjust_microsecond(tz) 49 |> microsecond_to_elixir() 50 end 51 52 defp microsecond_to_elixir(microsec) do 53 sec = div(microsec, 1_000_000) 54 microsec = rem(microsec, 1_000_000) 55 56 sec 57 |> :calendar.seconds_to_time() 58 |> Time.from_erl!({microsec, 6}) 59 end 60 end