cldr_dates_times icon indicating copy to clipboard operation
cldr_dates_times copied to clipboard

`Cldr.Interval` should handle interval of naive datetimes

Open sysashi opened this issue 2 months ago • 3 comments

Currently if you try to format an interval between two NaiveDateTime structs, the Cldr.Interval.to_string function will treat them as simple Date.

Example:

Cldr.Interval.to_string!(~U[2025-10-05 14:50:00Z], ~U[2025-10-05 16:30:00Z], FV.Cldr, format: :long) |> IO.puts
# => October 5, 2025, 2:50:00 PM UTC – 4:30:00 PM UTC

Cldr.Interval.to_string!(~N[2025-10-05 14:50:00], ~N[2025-10-05 16:30:00], FV.Cldr, format: :long) |> IO.puts
# => October 5, 2025

sysashi avatar Oct 08 '25 07:10 sysashi

Thanks for the report @sysashi.

I've published ex_cldr_dates_times 2.24.1 that partially addresses this issue. The remaining issue will be resolved when I fix #61 this month.

Partial fix done

With this partial fix you can now:

iex(1)> Cldr.Interval.to_string(~N[2025-10-05 14:50:00], ~N[2025-10-05 16:30:00], format: :short)
{:ok, "10/5/25, 2:50 PM\u2009–\u20094:30 PM"}
iex(2)> Cldr.Interval.to_string(~N[2025-10-05 14:50:00], ~N[2025-10-05 16:30:00], format: :medium)
{:ok, "Oct 5, 2025, 2:50:00 PM\u2009–\u20094:30:00 PM"}
iex(3)> Cldr.Interval.to_string(~N[2025-10-05 14:50:00], ~N[2025-10-05 16:30:00], format: :long)
{:error,
 {Cldr.DateTime.FormatError,
  "The format symbol 'z' requires at map with at least :zone_abbr. Found: ~N[2025-10-05 14:50:00 Cldr.Calendar.Gregorian]"}}

:long formats usually require time zone information

From this you can see that the :long format produces an error because the code will attempt to format both the date part and the time part separately and combine them. The time :long format, at least for the locales I checked, expects to have a time zone present. You can see the formats as follows. The z format code is the time zone.

iex(1)> Cldr.DateTime.Format.time_formats(:en)
{:ok,
 %Cldr.Time.Formats{
   short: %{unicode: "h:mm a", ascii: "h:mm a"},
   medium: %{unicode: "h:mm:ss a", ascii: "h:mm:ss a"},
   long: %{unicode: "h:mm:ss a z", ascii: "h:mm:ss a z"},
   full: %{unicode: "h:mm:ss a zzzz", ascii: "h:mm:ss a zzzz"}
 }}

Upcoming full fix

The work to resolve #61 is to seek a best match format in such cases. That work requires respinning the locale data so requires a new version of ex_cldr, and then a new version of ex_dates_times. The timing is such that CLDR 48 will also be out in October and I'm chasing that with ex_cldr updates too. Therefore I plan for a full fix to #61 and this #62 with CLDR 48 versions by the end of October. I hope thats ok for you.

Possible workaround

You can also specify your own interval format. It does mean you need to make decisions based upon the date times you're working with. Here is some template code to do that. Please comment here if you need more help with this.

def my_interval_format(from, to) do
  case Cldr.DateTime.Interval.greatest_difference(from, to) do
    {:ok, :H} -> "MMMM d, y h:mm - h:mm a"
    {:ok, :M} -> "MMMM d - d"
    ...
    {:error, :no_practical_difference} -> raise "No interval detected"
  end
end

Cldr.Interval.to_string(from, to, format: my_interval_format(from, to))

kipcole9 avatar Oct 08 '25 22:10 kipcole9

The :medium formatting option works for me (in my case that's the one I was planning to do anyway) and if anything i'll use the escape hatch you provided with formatting function.

Thanks for the explanation and links. The CLDR 48 update looks like a big chunk of work!

As always - thank you very much, you are the best! I don't remember any of my latest projects that don't use your libraries 😄

sysashi avatar Oct 09 '25 04:10 sysashi

Glad that works for you. Also glad some quite old code still stands up to scrutiny - haven't looked that for a few years I'd say.

I'm going to close this for now. Fixing #61 will solve the :long issue. But of course please reopen whenever necessary.

PS: Yes, there's quite a lot in CLDR 48 to accommodate. There usually is. My job is to try and make that mostly harmless to ex_cldr consumers :-)

kipcole9 avatar Oct 09 '25 20:10 kipcole9

@sysashi, I think this is OK to close as completed now. Let me know if you agree?

kipcole9 avatar Nov 10 '25 05:11 kipcole9

@kipcole9 agreed! I'm already using updated version, thank you! (sorry if I was responsible for closing this one)

sysashi avatar Nov 11 '25 13:11 sysashi