Skip to content

Instantly share code, notes, and snippets.

@talentdeficit
Created January 8, 2017 21:59
Show Gist options
  • Save talentdeficit/e9288c31e9fd4e56ee5f42b2f845a82a to your computer and use it in GitHub Desktop.
Save talentdeficit/e9288c31e9fd4e56ee5f42b2f845a82a to your computer and use it in GitHub Desktop.
timestamps with infinity
defmodule :posterize_xt_timestamp do
@moduledoc false
@behaviour Postgrex.Extension
import Postgrex.BinaryUtils, warn: false
use Postgrex.BinaryExtension, [send: "timestamp_send"]
@unix_epoch :calendar.datetime_to_gregorian_seconds({{1970, 1, 1}, {0, 0, 0}})
@gs_epoch :calendar.datetime_to_gregorian_seconds({{2000, 1, 1}, {0, 0, 0}})
@adjustment :erlang.convert_time_unit(@gs_epoch - @unix_epoch, :seconds, :microsecond)
@min_timestamp -211810204800000000
@max_timestamp 9223371331201000000
@infinity 9223372036854775807
@neg_infinity -9223372036854775808
def init(opts) do
case Keyword.get(opts, :units) do
nil -> :native
units when is_atom(units) -> units
end
end
def encode(units) do
quote location: :keep do
ts when is_integer(ts) or ts == :infinity or ts == :'-infinity' ->
:posterize_xt_timestamp.do_encode(unquote(units), ts)
other ->
raise ArgumentError, Postgrex.Utils.encode_msg(
other,
"an integer representing time since unix epoch in utc"
)
end
end
def do_encode(_units, :infinity) do
<< 8 :: int32, @infinity :: int64 >>
end
def do_encode(_units, :'-infinity') do
<< 8 :: int32, @neg_infinity :: int64 >>
end
def do_encode(units, timestamp) do
case :erlang.convert_time_unit(timestamp, units, :microsecond) - @adjustment do
ts when ts >= @min_timestamp and ts <= @max_timestamp ->
<< 8 :: int32, ts :: int64 >>
_ ->
raise ArgumentError, "timestamp is outside postgres' allowable range"
end
end
def decode(units) do
quote location: :keep do
<< 8 :: int32, microsecs :: int64 >> ->
:posterize_xt_timestamp.do_decode(unquote(units), microsecs)
end
end
def do_decode(units, microsecs) do
case microsecs do
@neg_infinity -> :'-infinity'
@infinity -> :infinity
_ -> :erlang.convert_time_unit(microsecs + @adjustment, :microsecond, units)
end
end
end
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment