elixir-map_diff icon indicating copy to clipboard operation
elixir-map_diff copied to clipboard

DateTime comparisons do not work

Open TylerPachal opened this issue 2 years ago • 5 comments

Here is an example of where DateTime values are not being considered equal, I suppose because under-the-hood a regular == comparison is being done:

# DateTime with no milliseconds
iex(1)> dt_1 = ~U[2020-01-01T00:00:00Z]    
~U[2020-01-01 00:00:00Z]

# DateTime with milliseconds
iex(2)> dt_2 = ~U[2020-01-01T00:00:00.000Z]
~U[2020-01-01 00:00:00.000Z]

# Putting them in maps, they are not equal
iex(3)> MapDiff.diff(%{dt: dt_1}, %{dt: dt_2})
%{
  added: %{dt: ~U[2020-01-01 00:00:00.000Z]},
  changed: :map_change,
  removed: %{dt: ~U[2020-01-01 00:00:00Z]},
  value: %{
    dt: %{
      added: ~U[2020-01-01 00:00:00.000Z],
      changed: :map_change,
      removed: ~U[2020-01-01 00:00:00Z],
      struct_name: DateTime,
      value: %{
        calendar: %{changed: :equal, value: Calendar.ISO},
        day: %{changed: :equal, value: 1},
        hour: %{changed: :equal, value: 0},
        microsecond: %{
          added: {0, 3},
          changed: :primitive_change,
          removed: {0, 0}
        },
        minute: %{changed: :equal, value: 0},
        month: %{changed: :equal, value: 1},
        second: %{changed: :equal, value: 0},
        std_offset: %{changed: :equal, value: 0},
        time_zone: %{changed: :equal, value: "Etc/UTC"},
        utc_offset: %{changed: :equal, value: 0},
        year: %{changed: :equal, value: 2020},
        zone_abbr: %{changed: :equal, value: "UTC"}
      }
    }
  }
}

# Comparing them using == they are not equal
iex(4)> dt_1 == dt_2
false

# Comparing them with DateTime.compare/2 works as expected
iex(5)> DateTime.compare(dt_1, dt_2)
:eq

Possible solutions

  1. Add an option for common structs like this to be compared using their own compare functions:
MapDiff.diff(map1, map2, compare_carefully: [DateTime, NaiveDateTime])
  1. Add a callback to let users do their own comparisons for specific types:
MapDiff.diff(map1, map2, compare: {DateTime, fn a, b -> DateTime.compare(a, b) == :eq end})

If you let me know what your preference is I can prepare a PR.

TylerPachal avatar Sep 22 '21 01:09 TylerPachal