Packages

Date-relative (and relatively universally unique) UUID generation. Based on https://github.com/recurly/druuid

Current section

Files

Jump to
druuid lib druuid.ex
Raw

lib/druuid.ex

defmodule Druuid do
use Bitwise
epoch = {{1970, 1, 1}, {0, 0, 0}}
@epoch :calendar.datetime_to_gregorian_seconds(epoch)
@doc """
This function generates a druuid id given an optional epoch_offset.
## Parameters
- `epoch_offset` (optional) integer value that offsets the 1970 epoch. Defaults to `0`.
"""
@spec gen(integer) :: integer
def gen(epoch_offset \\ 0) do
gen_from_values(epoch_offset, uniform, timestamp)
end
@doc """
This function calculates an epoch offset from the given datetime
## Parameters
- `offset_datetime` erlang datetime tuple from which to calculate the offset
## Examples
iex> Druuid.epoch_offset({{2016, 1, 1}, {0, 0, 0}})
1451606400
"""
@spec epoch_offset(Tuple) :: integer
def epoch_offset(offset_datetime) do
offset_datetime
|> :calendar.datetime_to_gregorian_seconds
|> -(@epoch)
end
@doc """
This function determinstically generates the druuid id
from the variables given. Unless you have a specific
reason to override one of these, you probably want to use
the Druuid.gen/1 function.
## Parameters
- `epoch_offset` integer value that offsets the 1970 epoch. Defaults to `0`.
- `rand` float value representing a random sample b/w 0 and 1 from a uniform distribution.
- `ts` integer timestamp representing the current time in seconds from the epoch.
## Examples
iex> Druuid.gen_from_values(0, 0.0, 1)
8388608000
"""
@spec gen_from_values(integer, float, integer) :: integer
def gen_from_values(epoch_offset, rand, ts) do
ms = ((ts - epoch_offset) * 1.0e+3) |> round
rand = rand * 1.0e+16 |> round
id = ms <<< (64 - 41)
v = :math.pow(2, (64 - 41)) |> round
id ||| rem(rand, v)
end
# Returns a uniform random number b/w 0.0 and 1.0.
# This should be changed to only seed the rng 1 time
defp uniform do
<< a :: 32, b :: 32, c :: 32 >> = :crypto.rand_bytes(12)
:random.seed(a,b,c)
:random.uniform
end
# Returns an integer representing the seconds since the epoch.
defp timestamp do
:calendar.universal_time
|> :calendar.datetime_to_gregorian_seconds
|> -(@epoch)
end
end