Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Use type modifier in time and timetz extensions #685

Merged
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
12 changes: 9 additions & 3 deletions lib/postgrex/extensions/time.ex
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,8 @@ defmodule Postgrex.Extensions.Time do
import Postgrex.BinaryUtils, warn: false
use Postgrex.BinaryExtension, send: "time_send"

@default_precision 6

def encode(_) do
quote location: :keep do
%Time{calendar: Calendar.ISO} = time ->
Expand All @@ -16,7 +18,7 @@ defmodule Postgrex.Extensions.Time do
def decode(_) do
quote location: :keep do
<<8::int32(), microsecs::int64()>> ->
unquote(__MODULE__).microsecond_to_elixir(microsecs)
unquote(__MODULE__).microsecond_to_elixir(microsecs, var!(mod))
end
end

Expand All @@ -28,12 +30,16 @@ defmodule Postgrex.Extensions.Time do
<<8::int32(), :calendar.time_to_seconds(time) * 1_000_000 + usec::int64()>>
end

def microsecond_to_elixir(microsec) do
def microsecond_to_elixir(microsec, precision) do
# use the default precision if the precision modifier from postgres is -1 (this means no precision specified)
# or if the precision is missing because we are in a super type which does not give us the sub-type's modifier
precision = if precision in [-1, nil], do: @default_precision, else: precision

sec = div(microsec, 1_000_000)
microsec = rem(microsec, 1_000_000)

sec
|> :calendar.seconds_to_time()
|> Time.from_erl!({microsec, 6})
|> Time.from_erl!({microsec, precision})
end
end
15 changes: 10 additions & 5 deletions lib/postgrex/extensions/timetz.ex
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ defmodule Postgrex.Extensions.TimeTZ do
use Postgrex.BinaryExtension, send: "timetz_send"

@day (:calendar.time_to_seconds({23, 59, 59}) + 1) * 1_000_000
@default_precision 6

def encode(_) do
quote location: :keep do
Expand All @@ -18,7 +19,7 @@ defmodule Postgrex.Extensions.TimeTZ do
def decode(_) do
quote location: :keep do
<<12::int32(), microsecs::int64(), tz::int32()>> ->
unquote(__MODULE__).microsecond_to_elixir(microsecs, tz)
unquote(__MODULE__).microsecond_to_elixir(microsecs, var!(mod), tz)
end
end

Expand All @@ -43,18 +44,22 @@ defmodule Postgrex.Extensions.TimeTZ do
<<12::int32(), :calendar.time_to_seconds(time) * 1_000_000 + usec::int64(), 0::int32()>>
end

def microsecond_to_elixir(microsec, tz) do
def microsecond_to_elixir(microsec, precision, tz) do
microsec
|> adjust_microsecond(tz)
|> microsecond_to_elixir()
|> microsecond_to_elixir(precision)
end

defp microsecond_to_elixir(microsec) do
defp microsecond_to_elixir(microsec, precision) do
# use the default precision if the precision modifier from postgres is -1 (this means no precision specified)
# or if the precision is missing because we are in a super type which does not give us the sub-type's modifier
precision = if precision in [-1, nil], do: @default_precision, else: precision

sec = div(microsec, 1_000_000)
microsec = rem(microsec, 1_000_000)

sec
|> :calendar.seconds_to_time()
|> Time.from_erl!({microsec, 6})
|> Time.from_erl!({microsec, precision})
end
end
10 changes: 10 additions & 0 deletions test/calendar_test.exs
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,11 @@ defmodule CalendarTest do
assert [[~T[01:02:03.123456]]] = query("SELECT time '01:02:03.123456'", [])
end

test "decode time with precision", context do
assert [[~T[00:00:00.000]]] = query("SELECT time(3) '00:00:00'", [])
assert [[[~T[00:00:00.000]]]] = query("SELECT ARRAY[time(3) '00:00:00']", [])
end

test "decode timetz", context do
assert [[~T[00:00:00.000000]]] = query("SELECT time with time zone '00:00:00 UTC'", [])
assert [[~T[01:02:03.000000]]] = query("SELECT time with time zone '01:02:03 UTC'", [])
Expand Down Expand Up @@ -53,6 +58,11 @@ defmodule CalendarTest do
assert [[~T[01:02:03.123456]]] = query("SELECT time with time zone '01:02:03.123456 UTC'", [])
end

test "decode timetz with precision", context do
assert [[~T[00:00:00]]] = query("SELECT time(0) with time zone '00:00:00 UTC'", [])
assert [[[~T[00:00:00]]]] = query("SELECT ARRAY[time(0) with time zone '00:00:00 UTC']", [])
end

test "decode date", context do
assert [[~D[0001-01-01]]] = query("SELECT date '0001-01-01'", [])
assert [[~D[0001-02-03]]] = query("SELECT date '0001-02-03'", [])
Expand Down
Loading