timestamptz.ex (2555B)
1 defmodule Postgrex.Extensions.TimestampTZ do 2 @moduledoc false 3 import Postgrex.BinaryUtils, warn: false 4 use Postgrex.BinaryExtension, send: "timestamptz_send" 5 6 @gs_epoch NaiveDateTime.to_gregorian_seconds(~N[2000-01-01 00:00:00.0]) |> elem(0) 7 8 @gs_unix_epoch NaiveDateTime.to_gregorian_seconds(~N[1970-01-01 00:00:00.0]) |> elem(0) 9 @us_epoch (@gs_epoch - @gs_unix_epoch) * 1_000_000 10 11 @gs_max elem(NaiveDateTime.to_gregorian_seconds(~N[9999-01-01 00:00:00.0]), 0) - @gs_unix_epoch 12 @us_max @gs_max * 1_000_000 13 14 @gs_min elem(NaiveDateTime.to_gregorian_seconds(~N[-4713-01-01 00:00:00.0]), 0) - @gs_unix_epoch 15 @us_min @gs_min * 1_000_000 16 17 @plus_infinity 9_223_372_036_854_775_807 18 @minus_infinity -9_223_372_036_854_775_808 19 20 def init(opts), do: Keyword.get(opts, :allow_infinite_timestamps, false) 21 22 def encode(_) do 23 quote location: :keep do 24 %DateTime{calendar: Calendar.ISO} = dt -> 25 unquote(__MODULE__).encode_elixir(dt) 26 27 other -> 28 raise DBConnection.EncodeError, 29 Postgrex.Utils.encode_msg(other, DateTime) 30 end 31 end 32 33 def decode(infinity?) do 34 quote location: :keep do 35 <<8::int32(), microsecs::int64()>> -> 36 unquote(__MODULE__).microsecond_to_elixir(microsecs, unquote(infinity?)) 37 end 38 end 39 40 ## Helpers 41 42 def encode_elixir(%DateTime{utc_offset: 0, std_offset: 0} = datetime) do 43 case DateTime.to_unix(datetime, :microsecond) do 44 microsecs when microsecs in @us_min..@us_max -> 45 <<8::int32(), microsecs - @us_epoch::int64()>> 46 47 _ -> 48 raise ArgumentError, "#{inspect(datetime)} is not in the year range -4713..9999" 49 end 50 end 51 52 def encode_elixir(%DateTime{} = datetime) do 53 raise ArgumentError, "#{inspect(datetime)} is not in UTC" 54 end 55 56 def microsecond_to_elixir(@plus_infinity, infinity?) do 57 if infinity?, do: :inf, else: raise_infinity("infinity") 58 end 59 60 def microsecond_to_elixir(@minus_infinity, infinity?) do 61 if infinity?, do: :"-inf", else: raise_infinity("-infinity") 62 end 63 64 def microsecond_to_elixir(microsecs, _infinity) do 65 DateTime.from_unix!(microsecs + @us_epoch, :microsecond) 66 end 67 68 defp raise_infinity(type) do 69 raise ArgumentError, """ 70 got \"#{type}\" from PostgreSQL. If you want to support infinity timestamps \ 71 in your application, you can enable them by defining your own types: 72 73 Postgrex.Types.define(MyApp.PostgrexTypes, [], allow_infinite_timestamps: true) 74 75 And then configuring your database to use it: 76 77 types: MyApp.PostgrexTypes 78 """ 79 end 80 end