Days value is not converting properly, it gives 61 days on reaching 30 days and higher values with more than 30

Days value is not calculating property, it gives 61 days on reaching 30 days. How to correct this. Solutions??

 defp time_display(date_joined) do
    date_joined = NaiveDateTime.to_date(date_joined)
    IO.inspect(date_joined, label: "date joined => ")

    years =
      Timex.diff(DateTime.utc_now() |> DateTime.to_date(), date_joined, :years)
      |> IO.inspect(label: "years => ")

    date_joined = if years == 0, do: date_joined, else: Timex.shift(date_joined, years: -years) |> IO.inspect(label: "date joined after years => ")

    months =
      Timex.diff(DateTime.utc_now() |> DateTime.to_date(), date_joined, :months)
      |> IO.inspect(label: "months => ")

      date_joined = if months == 0, do: date_joined, else: Timex.shift(date_joined, months: -months) |> IO.inspect(label: "date joined after months => ")

    **days =**
**     Timex.diff(DateTime.utc_now() |> DateTime.to_date(), date_joined, :days)**
**      |> IO.inspect(label: "days => ")**

    display(years, months, days)
  end

  defp display(0, 0, days), do: "#{days}" <> gettext(" days ")

  defp display(0, months, days), do: "#{months}" <> gettext(" months ") <> "#{days}" <> gettext(" days ")

  defp display(years, months, days),
    do:
      "#{years}" <>
        gettext(" years ") <> "#{months}" <> gettext(" months ") <> "#{days}" <> gettext(" days ")

In the Timex.shift calls, you should not invert the sign of the years or months. A negative value will shift the time backwards, while you want to shift it forwards.

This is a specific reply on the issue you encountered, and should solve it. That said, I suspect there are ways to improve the code, which looks a bit brittle at the moment.

You might, for example, try to split your logic into testable units. For start, you could separate calculation from formatting: you could split your logic into a function that, given two dates, calculates the time between them returning a tuple of {years, months, days}, and another function that, given such a tuple, formats it as a string for output. This way, you could easily test the calculation in isolation with different start/end dates, and catch bugs like the one you just found.

Adapting directly from your code:

def time_elapsed(from_date, to_date) do
  years = Timex.diff(to_date, from_date, :years)

  from_date = Timex.shift(from_date, years: years)
  months = Timex.diff(to_date, from_date, :months)

  from_date = Timex.shift(from_date, months: months)
  days = Timex.diff(to_date, from_date, :days)

  {years, months, days}
end

This function can be more easily tested with example dates, to assert that it works as you intend, even in corner cases such as months with different number of days, leap years, etc.

2 Likes