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]