Packages
postgrex
0.22.2
1.0.0-rc.1
retired
1.0.0-rc.0
retired
0.22.3
0.22.2
0.22.1
0.22.0
0.21.1
0.21.0
0.20.0
0.19.3
0.19.2
0.19.1
0.19.0
0.18.0
0.17.5
0.17.4
0.17.3
0.17.2
0.17.1
0.17.0
0.16.5
0.16.4
0.16.3
0.16.2
0.16.1
0.16.0
0.15.13
0.15.12
0.15.11
0.15.10
0.15.9
0.15.8
0.15.7
0.15.6
0.15.5
0.15.4
0.15.3
0.15.2
0.15.1
0.15.0
0.14.3
0.14.2
0.14.1
0.14.0
0.14.0-rc.1
0.14.0-rc.0
0.13.5
0.13.4
0.13.3
0.13.2
0.13.1
0.13.0
0.13.0-rc.0
0.12.2
0.12.1
0.12.0
0.11.2
0.11.1
0.11.0
0.10.0
0.9.1
0.9.0
0.8.4
0.8.3
0.8.2
0.8.1
0.8.0
0.7.0
0.6.0
0.5.5
0.5.4
0.5.3
0.5.2
0.5.1
0.5.0
0.4.2
PostgreSQL driver for Elixir
Security advisory:
This version has known vulnerabilities.
View advisories
Current section
Files
Jump to
Current section
Files
lib/postgrex/extensions/timestamptz.ex
defmodule Postgrex.Extensions.TimestampTZ do
@moduledoc false
import Postgrex.BinaryUtils, warn: false
use Postgrex.BinaryExtension, send: "timestamptz_send"
@gs_epoch NaiveDateTime.to_gregorian_seconds(~N[2000-01-01 00:00:00.0]) |> elem(0)
@gs_unix_epoch NaiveDateTime.to_gregorian_seconds(~N[1970-01-01 00:00:00.0]) |> elem(0)
@us_epoch (@gs_epoch - @gs_unix_epoch) * 1_000_000
@plus_infinity 9_223_372_036_854_775_807
@minus_infinity -9_223_372_036_854_775_808
@default_precision 6
# -1: user did not specify precision
# nil: coming from a super type that does not pass modifier for sub-type
@unspecified_precision [-1, nil]
def init(opts), do: Keyword.get(opts, :allow_infinite_timestamps, false)
def encode(_) do
quote location: :keep do
%DateTime{calendar: Calendar.ISO} = dt ->
unquote(__MODULE__).encode_elixir(dt)
other ->
raise DBConnection.EncodeError,
Postgrex.Utils.encode_msg(other, DateTime)
end
end
def decode(infinity?) do
quote location: :keep do
<<8::int32(), microsecs::int64()>> ->
unquote(__MODULE__).microsecond_to_elixir(microsecs, var!(mod), unquote(infinity?))
end
end
## Helpers
def encode_elixir(%DateTime{utc_offset: 0, std_offset: 0} = datetime) do
microsecs = DateTime.to_unix(datetime, :microsecond)
<<8::int32(), microsecs - @us_epoch::int64()>>
end
def encode_elixir(%DateTime{} = datetime) do
raise ArgumentError, "#{inspect(datetime)} is not in UTC"
end
def microsecond_to_elixir(@plus_infinity, _precision, infinity?) do
if infinity?, do: :inf, else: raise_infinity("infinity")
end
def microsecond_to_elixir(@minus_infinity, _precision, infinity?) do
if infinity?, do: :"-inf", else: raise_infinity("-infinity")
end
def microsecond_to_elixir(microsecs, precision, _infinity) do
split(microsecs, precision)
end
defp split(microsecs, precision) when microsecs < 0 and rem(microsecs, 1_000_000) != 0 do
secs = div(microsecs, 1_000_000) - 1
microsecs = 1_000_000 + rem(microsecs, 1_000_000)
split(secs, microsecs, precision)
end
defp split(microsecs, precision) do
secs = div(microsecs, 1_000_000)
microsecs = rem(microsecs, 1_000_000)
split(secs, microsecs, precision)
end
defp split(secs, microsecs, precision) do
precision = if precision in @unspecified_precision, do: @default_precision, else: precision
DateTime.from_gregorian_seconds(secs + @gs_epoch, {microsecs, precision})
end
defp raise_infinity(type) do
raise ArgumentError, """
got \"#{type}\" from PostgreSQL. If you want to support infinity timestamps \
in your application, you can enable them by defining your own types:
Postgrex.Types.define(MyApp.PostgrexTypes, [], allow_infinite_timestamps: true)
And then configuring your database to use it:
types: MyApp.PostgrexTypes
"""
end
end