How to generate uniq timestamp in mix test?

Hi,

I want to create X number of uniq timestamps in my test. However, mix test seems to run so fast :slight_smile: that it generates duplicate timestamps.

defmodule Tt do
  use ExUnit.Case

  test "test" do
    dates = 1..10
      |> Enum.map(fn _ -> DateTime.utc_now() |> DateTime.to_unix(:nanosecond) end)
      |> Enum.uniq()

    assert Enum.count(dates) == 10
  end
end

Random errors like so:

     Assertion with == failed
     code:  assert Enum.count(dates) == 10
     left:  8
     right: 10
     stacktrace:
       test/tt.exs:9: (test)

Is there a way to work around this?

What about:

defmodule Tt do
  use ExUnit.Case

  test "test" do
    now = DateTime.utc_now()
    dates = Enum.map(1..10, fn offset -> DateTime.add(now, offset, :nanosecond) end)
      
    assert Enum.count(dates) == 10
  end
end
2 Likes

It seems like DateTime.utc_now/1 has a maximum precision of microseconds:

                                 def utc_now(time_unit, calendar)                                  

  @spec utc_now(:native | :microsecond | :millisecond | :second, Calendar.calendar()) :: t()

So you’re attempting casting a value to nanoseconds which doesn’t have that precision in the first place:

Interactive Elixir (1.15.7) - press Ctrl+C to exit (type h() ENTER for help)
 iex(1)> DateTime.utc_now() |> DateTime.to_unix(:nanosecond)
1728805874862706000
 iex(2)> DateTime.utc_now() |> DateTime.to_unix(:nanosecond)
1728805875379239000
 iex(3)> DateTime.utc_now() |> DateTime.to_unix(:nanosecond)
1728805875813756000

If you just call System.os_time/1 instead, you will get the precision you are expecting:

Interactive Elixir (1.15.7) - press Ctrl+C to exit (type h() ENTER for help)
 iex(1)> System.os_time()
1728805768303333155

There also should be no duplicate values at this precision:

Interactive Elixir (1.15.7) - press Ctrl+C to exit (type h() ENTER for help)
 iex(1)> Enum.map(1..10_000_000, fn _i -> System.os_time() end) |> Enum.uniq() |> length()
10000000

If I was being cautious, and I wanted a lot of timestamps that were guaranteed to be unique, I would just create one then generate the rest:

Interactive Elixir (1.15.7) - press Ctrl+C to exit (type h() ENTER for help)
 iex(1)> timestamp = System.os_time()
1728806449550382270
 iex(2)> Enum.map(1..10, fn i -> timestamp - i end) 
[1728806449550382269, 1728806449550382268, 1728806449550382267, 1728806449550382266,
 1728806449550382265, 1728806449550382264, 1728806449550382263, 1728806449550382262,
 1728806449550382261, 1728806449550382260]
2 Likes