a
|> time
|> traveller
I want to freeze time for my test. So I came up with the following:
defmodule Chronos do
alias __MODULE__, as: Chronos
use Agent
@mty_tz "America/Monterrey"
@time_format "%Y-%m-%dT%H:%M:%S"
@spec start_link(list()) :: {:error, any()} | {:ok, pid()}
def start_link([]) do
Agent.start_link(
fn ->
%{is_frozen: false, frozen_value: nil}
end,
name: Chronos
)
end
@spec now() :: DateTime.t()
def now do
get_timestamp()
|> Timex.from_unix()
|> shift_localtime
catch
:exit, {:noproc, _} ->
Timex.now(@mty_tz)
|> shift_localtime
end
@spec get_timestamp() :: integer()
def get_timestamp do
state = Agent.get(Chronos, fn state -> state end)
if state[:is_frozen] do
state[:frozen_value]
else
:os.system_time(:second)
end
end
@spec freeze() :: atom()
def freeze do
freeze(:os.system_time(:second))
end
@spec freeze(integer()) :: atom()
def freeze(timestamp) do
Agent.update(Chronos, fn _state ->
%{is_frozen: true, frozen_value: timestamp}
end)
end
@spec unfreeze() :: atom()
def unfreeze do
Agent.update(Chronos, fn _state ->
%{is_frozen: false, frozen_value: nil}
end)
end
defp shift_localtime(date_time) do
date_time
|> Timex.format!(@time_format, :strftime)
|> Timex.parse!(@time_format, :strftime)
|> Timex.to_datetime()
end
end
The tiny module depends on Timex. Here are some use cases:
### Within your test, define
defp freeze_time(_context) do
# 2021-08-12 22:25:00-06:00 -06
Chronos.freeze(1_628_828_700)
end
### And use it at your setup
setup [:freeze_time]
### Now, wherever you call for `Chronos.now()` you will get the frozen datetime.
iex(1)> Chronos.now()
~U[2021-08-13 04:25:00Z]
And now I finished this tiny example.